1. 程式人生 > 其它 >django orm 聯表查詢優化

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部落格