zoukankan      html  css  js  c++  java
  • 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博客

  • 相关阅读:
    学习记录---KMP算法-部分匹配表理解
    关于GameObject无法禁用问题
    out用法
    关于Dictionary.TryGetValue的个人理解记录
    Transform.parent和Transform.root的区别
    Queue默认容量
    关于Camera Culling Mask
    MSVCP110.DLL没有被指定在WINDOWS上运行
    typeof instanceof 之间的区别总结
    Promise 使用心得
  • 原文地址:https://www.cnblogs.com/fengzaoye/p/14927057.html
Copyright © 2011-2022 走看看