By khoanc, at: 2024年6月1日10:52

Estimated Reading Time: __READING_TIME__ minutes

Tackling the N+1 Queries Problem in Django
Tackling the N+1 Queries Problem in Django

DjangoにおけるN+1クエリ問題への対処

熟練の開発者として、N+1クエリ問題の煩わしさはよく理解しています。これは、コードがリストに対して1つのクエリを実行し、そのリスト内の各項目に対して追加のクエリを実行する、パフォーマンスを低下させる厄介な問題です。Djangoでこの問題を検出して修正し、アプリケーションの動作をよりスムーズで高速にする方法を見ていきましょう。

 

N+1クエリの理解

各区に属する区のリストがあると想像してください。区とその区の情報を取り出す方法は次のようになります。

wards = Ward.objects.all()
for ward in wards:
    name = ward.district.name


これにより、区に対する1つのクエリと、各区の区名に対する1つのクエリが生成され、N+1の問題につながります。

 

N+1クエリの検出

Django Debug ToolbarSentry、およびScout APM などのツールは、これらの問題の特定に役立ちます。

たとえば、Django Debug Toolbarは、ページでの過剰なクエリを強調表示します。私たちのGlintecoチームは、この投稿ですでに多くの便利なDjangoパッケージを指摘しています。

 

select_related()を使用した解決

外部キーの関係では、select_related()を使用してSQL結合を実行し、関連オブジェクトを1つのクエリで取得します。

wards = Ward.objects.select_related('district').all()
for ward in wards:
    print(ward.district.name)


これにより、各区のdistrictフィールドがプリロードされ、クエリの数が削減されます。

 

prefetch_related()を使用した解決

多対多の関係と逆外部キーの関係には、prefetch_related()が役立ちます。これは、個別の検索を実行し、Pythonで「結合」を行います。

districts = District.objects.prefetch_related('wards').all()
for district in district:
    wards = district.wards.all()


これにより、区の数に関係なく、すべての区のすべての区が2つのクエリで取得されます。

 

実践例

著者とその著書があるとします。効率的に取得するには、次のようになります。

authors = Author.objects.prefetch_related('book_set').all()
for author in authors:
    books = author.book_set.all()


結論

select_related()prefetch_related()を活用することで、N+1クエリの問題を軽減し、Djangoアプリケーションのパフォーマンスを大幅に向上させることができます。 Django Debug ToolbarSentry、およびScout APM などのツールは、これらの問題を検出する上で非常に役立ちます。

Tag list:
- Django
- Django Queryset
- Django Manager
- Django Query
- Django N+1 Queries
- Django Query Optimization
- Query Optimization

Related

Python Unit Test

Read more
Django Python

Read more
Python

Read more
Subscribe

Subscribe to our newsletter and never miss out lastest news.