工作中项目优化小记

前言

博主目前接手了一个项目,该项目代码从某马微店那里买来,社交金融相关。这个项目的代码可以说是逻辑混乱,毫无可重用性,而且又无文档,git message中一堆毫无用处的“update”,PEP8规范?不存在的。使得我每日游走在被代码劝退的边缘。

震惊!一次请求1W多条sql。

该功能为商品的热销分类表单。之前大概看了一眼,数据是先从redis中取,redis值存的永久有效,没有的话查DB。然后某天清了一下测试数据库的redis,这下好了,接口超时。查了一下silk发现运行了1w多次sql。

万恶之源的代码,难怪查了1W多次,这外键coupon,每次都要查询数据库。而且这里channel也没有用set,这简直基础数据结构都没掌握啊。

1
2
3
4
5
6
tbkcoupons = TbkCouponTags.objects.filter(is_hot=True)
channel = []
for t in tbkcoupons:
cc = t.coupon.channel
if cc not in channel:
channel.append(cc)

使用select_related简单的处理了一下

1
2
3
4
5
tbkcoupons = TbkCouponTags.objects.select_related('coupon').filter(is_hot=True)
channel = set()
for t in tbkcoupons:
cc = t.coupon.channel
channel.add(cc)

快了10倍,冷静一下,思考一下这大哥到底要干嘛,后面看到拿到了tbkcoupons没有再做其他用处。所以这东西完全可以使用反向连接加去重来解决啊,看来sql基础也欠缺。这里改成了values+distinct来处理,因为mysql不支持distinct(‘channel’)这种指定字段的形式。

1
2
tbkcoupons = TbkCoupon.objects.filter(tbkcoupontags__is_hot=True).values('channel').distinct()
channels = [c['channel'] for c in tbkcoupons]

后面的逻辑判断,又使用了一堆的ifelse,看着不清晰,将其改为了字典。这些倒不是什么大问题。

最后,修改了一些取值的逻辑。最后将结果保存到了redis中。命中缓存的话大概10+ms。

后来又发现了一个有意思的事情,他好像也知道这个查询挺慢的,所以用celerybeat实现了一个周期任务,定时地更新这个redis。

返回按照当前查询数组顺序的queryset。

遇见了这样一个需求 ,收藏列表需要返回按照收藏顺序的商品。而这个顺序保存在java后台。通过Thrift接口调用,返回了一个id列表,而商品保存在我这里。需要根据这个id的顺序返回queryset。django-get-a-queryset-from-array-of-ids-in-specific-order

1
2
3
from django.db.models import Case, When
preserved = Case(*[When(num_iid=num_iid, then=pos) for pos, num_iid in enumerate(num_iids)])
TbkCoupon.objects.filter(num_iid__in=num_iids).order_by(preserved)