django orm 聯表查詢優化
阿新 • • 發佈:2021-06-24
背景: 在某個查詢系統下,比如有如下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部落格