Django中select_related查詢優(yōu)化的具體實現(xiàn)
目標
- 理解 N+1 查詢問題及其性能影響
- 掌握
select_related的作用和使用方法 - 能夠分析 Django ORM 生成的 SQL 語句
- 在實際開發(fā)中正確應(yīng)用查詢優(yōu)化技巧
一、問題引入:N+1 查詢問題
1.1 數(shù)據(jù)模型定義
# models.py
class Author(models.Model):
name = models.CharField(max_length=100)
country = models.CharField(max_length=50)
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
publish_date = models.DateField()
1.2 存在性能問題的代碼
# views.py (存在N+1查詢問題)
books = Book.objects.all()
for book in books:
print(f"書名:{book.title}, 作者:{book.author.name}")
1.3 對應(yīng)的 SQL 查詢
-- 第一次查詢:獲取所有書籍
SELECT
"library_book"."id",
"library_book"."title",
"library_book"."author_id",
"library_book"."publish_date"
FROM "library_book";
-- 后續(xù) N 次查詢:為每本書單獨查詢作者
SELECT "library_author"."id", "library_author"."name", "library_author"."country"
FROM "library_author" WHERE "library_author"."id" = 1;
SELECT "library_author"."id", "library_author"."name", "library_author"."country"
FROM "library_author" WHERE "library_author"."id" = 2;
-- ... 依此類推,產(chǎn)生 N+1 次查詢
問題分析:如果有 100 本書,將產(chǎn)生 101 次數(shù)據(jù)庫查詢,嚴重影響性能。
二、解決方案:select_related
2.1select_related的基本用法
# views.py (優(yōu)化后的代碼)
books = Book.objects.select_related('author').all()
for book in books:
print(f"書名:{book.title}, 作者:{book.author.name}")
2.2 優(yōu)化后的 SQL
SELECT
"library_book"."id",
"library_book"."title",
"library_book"."author_id",
"library_book"."publish_date",
-- 通過 JOIN 一次性獲取作者信息
"library_author"."id",
"library_author"."name",
"library_author"."country"
FROM "library_book"
INNER JOIN "library_author"
ON ("library_book"."author_id" = "library_author"."id");
優(yōu)化效果:無論有多少本書,都只執(zhí)行 1 次 數(shù)據(jù)庫查詢。
三、select_related的深入使用
3.1 深度關(guān)聯(lián)查詢
擴展數(shù)據(jù)模型:
class Country(models.Model):
name = models.CharField(max_length=50)
code = models.CharField(max_length=3)
class Author(models.Model):
name = models.CharField(max_length=100)
country = models.ForeignKey(Country, on_delete=models.CASCADE)
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
publish_date = models.DateField()
深度關(guān)聯(lián)查詢:
# 一次性獲取 Book -> Author -> Country 的所有數(shù)據(jù)
books = Book.objects.select_related('author__country').all()
for book in books:
print(f"{book.title} - {book.author.name} - {book.author.country.name}")
對應(yīng)的 SQL:
SELECT
"library_book"."id",
"library_book"."title",
"library_book"."author_id",
"library_book"."publish_date",
-- 作者表字段
"library_author"."id",
"library_author"."name",
"library_author"."country_id",
-- 國家表字段(通過第二次 JOIN)
"library_country"."id",
"library_country"."name",
"library_country"."code"
FROM "library_book"
INNER JOIN "library_author"
ON ("library_book"."author_id" = "library_author"."id")
INNER JOIN "library_country"
ON ("library_author"."country_id" = "library_country"."id");
四、實踐技巧與注意事項
4.1 查看生成的 SQL 語句
方法1:使用 query 屬性
books = Book.objects.select_related('author')
print(books.query)
方法2:配置 Django SQL 日志
在 settings.py 中添加:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'level': 'DEBUG',
},
},
}
4.2 使用原則
適用場景:
- 一對一關(guān)系(OneToOneField)
- 多對一關(guān)系(ForeignKey)
- 需要立即訪問關(guān)聯(lián)對象字段時
不適用場景:
- 多對多關(guān)系(ManyToManyField)
- 反向的多對一關(guān)系
- 對于這些情況,應(yīng)該使用
prefetch_related
注意事項:
- 不要過度使用深度關(guān)聯(lián)
- 考慮查詢返回的數(shù)據(jù)量
- 只在確實需要訪問關(guān)聯(lián)對象時使用
五、總結(jié)對比
5.1 性能對比表
| 查詢方式 | 查詢次數(shù) | 適用場景 | SQL 類型 |
|---|---|---|---|
| 普通查詢 | N+1 次 | 不確定是否需要關(guān)聯(lián)對象 | 簡單查詢 + 多次關(guān)聯(lián)查詢 |
select_related | 1 次 | 確定需要訪問關(guān)聯(lián)對象的所有字段 | JOIN 查詢 |
5.2 核心要點
- 解決問題:N+1 查詢問題
- 實現(xiàn)原理:使用 SQL JOIN 語句一次性加載關(guān)聯(lián)數(shù)據(jù)
- 使用語法:
Model.objects.select_related('foreign_key_field') - 深度關(guān)聯(lián):使用雙下劃線
'field__related_field' - 記憶口訣:“往前拿,用
select_related”
5.3 最佳實踐
# 好的實踐:明確知道需要作者信息
books = Book.objects.select_related('author').filter(publish_date__year=2023)
for book in books:
# 這里會頻繁訪問 author 的字段
display_info = f"{book.title} by {book.author.name}"
# 不好的實踐:不確定是否需要關(guān)聯(lián)對象
books = Book.objects.select_related('author').filter(publish_date__year=2023)
for book in books:
# 如果這里根本不訪問 book.author,就浪費了 JOIN 的性能
print(book.title)
到此這篇關(guān)于Django中select_related查詢優(yōu)化的具體實現(xiàn)的文章就介紹到這了,更多相關(guān)Django select_related查詢優(yōu)化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
深入理解Python內(nèi)置函數(shù)eval的使用
在Python中,eval函數(shù)是一個內(nèi)置函數(shù),用于將字符串解析并執(zhí)行為Python表達式,本文將詳細介紹eval函數(shù)的使用方法和注意事項,需要的可以參考一下2023-06-06
Python Sweetviz輕松實現(xiàn)探索性數(shù)據(jù)分析
Sweetviz是一個開放源代碼Python庫,可生成精美的高密度可視化文件,以單行代碼啟動EDA(探索性數(shù)據(jù)分析)。輸出是一個完全獨立的HTML應(yīng)用程序,該系統(tǒng)圍繞快速可視化目標值和比較數(shù)據(jù)集而構(gòu)建。其目標是幫助快速分析目標特征,訓(xùn)練與測試數(shù)據(jù)以及其他此類數(shù)據(jù)表征任務(wù)2021-11-11
Python3時間轉(zhuǎn)換之時間戳轉(zhuǎn)換為指定格式的日期方法詳解
這篇文章主要介紹了Python3時間轉(zhuǎn)換之時間戳轉(zhuǎn)換為指定格式的日期,需要的朋友可以參考下2021-04-04
Python自然語言處理 NLTK 庫用法入門教程【經(jīng)典】
這篇文章主要介紹了Python自然語言處理 NLTK 庫用法,結(jié)合實例形式詳細分析了NLTK庫的功能、安裝、引用以及使用NLTK庫進行文本分析的各種常用操作技巧,需要的朋友可以參考下2018-06-06
python matplotlib坐標軸設(shè)置的方法
本篇文章主要介紹了python matplotlib坐標軸設(shè)置的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-12-12
使用Python轉(zhuǎn)換Markdown文件為Word文檔
Markdown格式的文章轉(zhuǎn)換成Word文檔能夠幫助作者制作更復(fù)雜的文檔,同時確保內(nèi)容的一致性和美觀性,本文將使用Python實現(xiàn)Markdown轉(zhuǎn)Word,感興趣的可以了解下2024-11-11

