ホームページ >バックエンド開発 >Python チュートリアル >AppSignal を使用して Django のクエリを見つけて修正する
この記事では、N 1 クエリ、AppSignal でそれらを検出する方法、およびそれらを修正して Django アプリを大幅に高速化する方法について学びます。
理論的な側面から始めて、次に実践的な例に進みます。実際の例は、運用環境で遭遇する可能性のあるシナリオを反映しています。
始めましょう!
N 1 クエリの問題は、データベースと対話する Web アプリケーションで一般的なパフォーマンスの問題です。これらのクエリは重大なボトルネックを引き起こす可能性があり、データベースが成長するにつれてボトルネックはさらに悪化します。
この問題は、オブジェクトのコレクションを取得し、コレクション内の各項目の関連オブジェクトにアクセスするときに発生します。たとえば、書籍のリストを取得するには 1 つのクエリ (1 クエリ) が必要ですが、各書籍の著者にアクセスすると、アイテムごとに追加のクエリ (N クエリ) がトリガーされます。
N 1 の問題は、データベース内のデータを作成または更新するときにも発生する可能性があります。たとえば、Bulk_create() やBulk_update() などのメソッドを使用するのではなく、ループを繰り返してオブジェクトを個別に作成または更新すると、過剰なクエリが発生する可能性があります。
多数の小さなクエリを実行すると、操作を少数の大きなクエリに統合するよりも大幅に時間がかかり、リソースを大量に消費するため、N 1 クエリは非常に非効率です。
Django のデフォルトの QuerySet の動作は、特に QuerySet がどのように機能するかを知らない場合、不注意で N 1 の問題を引き起こす可能性があります。 Django のクエリセットは遅延型です。つまり、QuerySet が評価されるまでデータベース クエリは実行されません。
次のものが揃っていることを確認してください。
注: このプロジェクトのソース コードは、appsignal-django-n-plus-one GitHub リポジトリにあります。
書籍管理 Web アプリを使用して作業します。この Web アプリは、N 1 クエリの問題とその解決方法を示すために構築されています。
まず、GitHub リポジトリのベース ブランチを複製します。
$ git clone git@github.com:duplxey/appsignal-django-n-plus-one.git \ --single-branch --branch base && cd appsignal-django-n-plus-one
次に、仮想環境を作成してアクティブ化します。
$ python3 -m venv venv && source venv/bin/activate
要件をインストールします:
(venv)$ pip install -r requirements.txt
データベースを移行してデータを追加します:
(venv)$ python manage.py migrate (venv)$ python manage.py populate_db
最後に、開発サーバーを起動します:
(venv)$ python manage.py runserver
お気に入りの Web ブラウザを開いて、http://localhost:8000/books に移動します。 Web アプリは、データベースから 500 冊の書籍の JSON リストを返す必要があります。
Django 管理サイトには http://localhost:8000/admin からアクセスできます。管理者の資格情報は次のとおりです:
user: username pass: password
Django プロジェクトに AppSignal をインストールするには、公式ドキュメントに従ってください:
開発サーバーを再起動して、すべてが機能することを確認します。
$ git clone git@github.com:duplxey/appsignal-django-n-plus-one.git \ --single-branch --branch base && cd appsignal-django-n-plus-one
アプリは自動的にデモ エラーを AppSignal に送信する必要があります。この時点から、すべてのエラーは AppSignal に送信されます。さらに、AppSignal はアプリのパフォーマンスを監視し、問題を検出します。
N 1 クエリを修正するための前提条件は、アプリのデータベース スキーマを理解することです。モデルの関係に細心の注意を払ってください。潜在的な N 1 の問題を正確に特定するのに役立ちます。
Web アプリには、Author と Book という 2 つのモデルがあり、1 対多 (1:M) の関係を共有します。これは、各書籍が 1 人の著者に関連付けられている一方で、1 人の著者を複数の書籍にリンクできることを意味します。
どちらのモデルにも、モデル インスタンスを JSON にシリアル化するための to_dict() メソッドがあります。それに加えて、Book モデルはディープ シリアル化 (本の著者だけでなく本のシリアル化) を使用します。
モデルはbooks/models.pyで定義されています:
$ python3 -m venv venv && source venv/bin/activate
次に、次のように、books/admin.py の Django 管理サイトに登録されます。
(venv)$ pip install -r requirements.txt
AuthorAdmin は BookInline を使用して、著者の管理ページ内に著者の書籍を表示することに注意してください。
Web アプリは次のエンドポイントを提供します:
開発 Web サーバーが実行されている場合、上記のリンクをクリックできます。
そしてそれらはbooks/views.pyで次のように定義されています:
(venv)$ python manage.py migrate (venv)$ python manage.py populate_db
なるほど、Web アプリがどのように機能するかがわかりました!
次のセクションでは、AppSignal で N 1 個のクエリを検出するためにアプリのベンチマークを行い、コードを変更してそれらを排除します。
AppSignal を使用するとパフォーマンスの問題を検出するのは簡単です。必要なのは、通常どおりアプリを使用/テストすることだけです (たとえば、すべてのエンドポイントにアクセスして応答を検証することでエンドユーザー テストを実行します)。
エンドポイントがヒットすると、AppSignal はそのパフォーマンス レポートを作成し、関連するすべての訪問をグループ化します。各訪問はエンドポイントのレポートにサンプルとして記録されます。
まず、アプリのすべてのエンドポイントにアクセスしてパフォーマンス レポートを生成します。
次に、AppSignal ダッシュボードを使用して、遅いエンドポイントを分析しましょう。
AppSignal アプリに移動し、パフォーマンス > を選択します。サイドバーの課題リスト。次に、平均 をクリックして、平均応答時間の降順で問題を並べ替えます。
最も遅いエンドポイント (books/) をクリックして詳細を表示します。
最新のサンプルを見ると、このエンドポイントが 1090 ミリ秒で応答を返していることがわかります。グループの内訳を見ると、SQLite は 651 ミリ秒かかるのに対し、Django は 439 ミリ秒かかっています。
これほど単純なエンドポイントにはそれほど時間はかからないはずなので、これは問題を示しています。
何が起こったかの詳細を確認するには、サイドバーで サンプル を選択し、最新のサンプルを選択してください。
イベント タイムラインまで下にスクロールして、実行された SQL クエリを確認します。
query.sql テキストの上にマウスを置くと、実際の SQL クエリが表示されます。
1000 を超えるクエリが実行されました:
$ git clone git@github.com:duplxey/appsignal-django-n-plus-one.git \ --single-branch --branch base && cd appsignal-django-n-plus-one
これらは、N 1 クエリの明らかな兆候です。最初のクエリは書籍 (1) を取得し、後続の各クエリは書籍の著者の詳細 (N) を取得しました。
これを修正するには、books/views.py に移動し、book_list_view() を次のように変更します。
$ python3 -m venv venv && source venv/bin/activate
Django の select_relative() メソッドを利用することで、最初のクエリで追加の関連オブジェクト データ (つまり、作成者) を選択します。 ORM は SQL 結合を利用するようになり、最終的なクエリは次のようになります:
(venv)$ pip install -r requirements.txt
開発サーバーが再起動するまで待ち、影響を受けるエンドポイントを再テストします。
再度ベンチマークを実行した後、応答時間は 1090 から 45 に、クエリ数は 1024 から 2 に減少しました。これは、それぞれ 24 倍と 512 倍の改善です。
次に、2 番目に遅いエンドポイント (書籍/著者別/) を見てみましょう。
前のステップで行ったようにダッシュボードを使用して、エンドポイントの SQL クエリを検査します。このエンドポイントでは、類似しているがそれほど深刻ではない N 1 パターンに気づくでしょう。
Django は頻繁に実行される SQL クエリ (つまり、本の著者を繰り返し取得する) をキャッシュするのに十分な機能を備えているため、このエンドポイントのパフォーマンスはそれほど深刻ではありません。 Django キャッシュの詳細については、公式ドキュメントを参照してください。
books/views.py の prefetch_popular() を利用してエンドポイントを高速化しましょう:
$ git clone git@github.com:duplxey/appsignal-django-n-plus-one.git \ --single-branch --branch base && cd appsignal-django-n-plus-one
前のセクションでは、select_popular() メソッドを使用して 1 対 1 の関係 (各書籍には単一の著者が存在します) を処理しました。ただし、この場合は 1 対多の関係 (著者は複数の本を持つことができる) を処理しているため、prefetch_popular() を使用する必要があります。
これら 2 つのメソッドの違いは、select_popular() は SQL レベルで動作するのに対し、prefetch_popular() は Python レベルで最適化することです。後者の方法は、多対多の関係にも使用できます。
詳細については、prefetch_popular() に関する Django の公式ドキュメントを参照してください。
ベンチマーク後、応答時間は 90 ミリ秒から 44 ミリ秒に短縮され、クエリ数は 32 から 4 に減少しました。
Django 管理サイトでの N 1 クエリの検出も同様に機能します。
まず、管理者サイトにログインし、パフォーマンス レポートを生成します (たとえば、いくつかの著者や書籍を作成し、更新し、削除します)。
次に、AppSignal アプリのダッシュボードに移動し、今回は管理者ごとに問題をフィルターします。
私の場合、最も遅いエンドポイントは次の 2 つです:
/admin/login については、Django によって完全に処理されるため、多くのことはできません。そのため、2 番目に遅いエンドポイントに焦点を当てましょう。これを検査すると、N 1 クエリの問題が明らかになります。著者は書籍ごとに個別に取得されます。
これを修正するには、BookInline の get_queryset() をオーバーライドして、最初のクエリで著者の詳細を取得します。
$ python3 -m venv venv && source venv/bin/activate
もう一度ベンチマークを実行し、クエリ数が減少していることを確認します。
この投稿では、AppSignal を使用した Django での N 1 クエリの検出と修正について説明しました。
ここで学んだことを活用すると、Django Web アプリを大幅に高速化できます。
覚えておくべき最も重要な 2 つのメソッドは、select_popular() と prefetch_popular() です。 1 つ目は 1 対 1 の関係に使用され、2 つ目は 1 対多および多対多の関係に使用されます。
コーディングを楽しんでください!
追伸Python の投稿を報道後すぐに読みたい場合は、Python Wizardry ニュースレターを購読して、投稿を 1 つも見逃さないようにしてください。
以上がAppSignal を使用して Django のクエリを見つけて修正するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。