By khoanc, at: 2024年6月1日10:52
Estimated Reading Time: __READING_TIME__ minutes


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 Toolbar、Sentry、および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 Toolbar、Sentry、およびScout APM などのツールは、これらの問題を検出する上で非常に役立ちます。