django orm 联表查询优化
背景: 在某个查询系统下,比如有如下3个表,在对获取查询条件为user_id和时间范围对LogRecord查询对应数据并且拿到用户id以及日志详情title等。
class ActionUser(models.Model): """ 操作人用户名 """ user_id = models.IntegerField(verbose_name="用户id", unique=True, null=False) name = models.CharField(verbose_name="用户名", max_length=128, null=False) class LogDetail(models.Model): """ 日志详情 """ title = models.CharField(verbose_name="日志标题", max_length=256, null=False) content = models.TextField(verbose_name="详细内容", default="") class LogRecord(models.Model): """ 操作日志记录表 """ # user = models.CharField(verbose_name="操作人用户名", max_length=) user = models.ForeignKey(ActionUser, verbose_name="用户", on_delete=models.CASCADE) time = models.IntegerField(verbose_name="日志创建时间戳(秒级)", null=False, db_index=True) ACTION_CHOICES = ( # cud操作类型记录 (1, "创建"), (2, "更新"), (3, "删除") ) action = models.IntegerField(null=False, choices=ACTION_CHOICES, verbose_name="操作类型") detail = models.ForeignKey(LogDetail, verbose_name='操作详情', on_delete=models.CASCADE) created_time = models.DateTimeField(auto_now_add=True, verbose_name="数据创建时间")
一般的操作可能就是:
objs = LogRecord.objects.filter(user__user_id=user_id, time__gte=start_time, time__lte=end_time).order_by('-id') data = [] for i in objs: data.append({ "log_id": i.id, "user_id": i.user.user_id, "user_name": "%s[%s]" % (i.user.name, str(i.user.user_id)), "log_time": timestamp_to_time_cum(i.time), "action": action_static[i.action], "title": i.detail.title })
这样的话数据量大概在几千条以上需要10多秒的查询时间。
调试时发现主要耗时是在获取外键字段内容的时候耗时最多,其实objs查出来时也在毫秒级内。
看怎么解决对外键内容的查询优化?而且又是可以用到orm?
这时候就想到了使用select_related, prefetch_related
select_related(self, *fields)
性能相关:表之间进行join连表操作,一次性获取关联的数据。 model.tb.objects.all().select_related() model.tb.objects.all().select_related('外键字段') model.tb.objects.all().select_related('外键字段__外键字段')
prefetch_related(self, *lookups)
性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询 在内存中做关联,而不会再做连表查询 # 第一次 获取所有用户表 # 第二次 获取用户类型表where id in (用户表中的查到的所有用户ID) models.UserInfo.objects.prefetch_related('外键字段')
这次使用select_related进行调试:
LogRecord.objects.filter(user__user_id=user_id, time__gte=start_time, time__lte=end_time).select_related('user', 'detail').order_by('-id')
这次的查询结果是:
查询出3000多条数据,查询时间是在毫秒级,足足提升了40倍左右!!
这次优化基本满足预期~
后续还需对更大的数据层级考量性能问题!
tips:
connection.queries
只有在调试为真时。它是一个按查询执行顺序排列的字典列表。
>>> from django.db import connection >>> connection.queries [] >>> Author.objects.all() <QuerySet [<Author: Author object>]> >>> connection.queries [{u'time': u'0.002', u'sql': u'SELECT "library_author"."id", "library_author"."name" FROM "library_author" LIMIT 21'}]
可用这个对orm查询做调试分析。
感谢参考:
Django ORM性能优化,数据存取优化 - 简书 (jianshu.com)
Django ORM 进行查询操作和性能优化_weixin_30376453的博客-CSDN博客