web-dev-qa-db-ja.com

Django ORMでiterator()を使用するか使用しないか

これは queryset iterator() methodのDjangoドキュメント からです:

QuerySetは通常、その結果を内部的にキャッシュするため、繰り返し評価を行っても追加のクエリは発生しません。対照的に、iterator()はQuerySetレベルでキャッシュを行わずに結果を直接読み取ります(内部では、デフォルトの反復子はiterator()を呼び出して戻り値をキャッシュします)。一度だけアクセスする必要がある多数のオブジェクトを返すQuerySetの場合、これによりパフォーマンスが向上し、メモリが大幅に削減されます。

読んだ後も、まだ混乱しています。パフォーマンスの向上とメモリの削減に関する行は、iterator()メソッドを使用する必要があることを示唆しています。誰かが良い例と悪い例のiterator()の使用例をいくつか挙げられますか?

クエリ結果がキャッシュされていない場合でも、実際にモデルに複数回アクセスしたい場合は、誰かが次のことを実行できませんか?

saved_queries = list(Model.objects.all().iterator())
21
Lucas Ou-Yang

呼び出す文の最初の部分に注意してください:_For a QuerySet which returns a large number of objects that you only need to access once_

つまり、これとは逆に、一連の結果を再利用する必要があり、メモリの問題を引き起こすほど多くない場合は、iteratorを使用しないでください。追加のデータベースラウンドトリップはalwaysであるため、キャッシュされた結果を使用するよりもパフォーマンスが低下します。

QuerySetを強制的にリストに評価することもできますが、次のようになります。

  • 単なるsaved_queries = Model.objects.all()よりも多くのタイピングが必要です
  • webページで結果をページ分割していると言います:後続のページ編集者が必要な20件の結果のスライスを選択できるようにするのではなく、すべての結果を強制的にメモリに入れます(可能なメモリ問題に戻ります)。
  • QuerySets are lazy なので、たとえば、すべてのリクエストのコンテキストにQuerySetを挿入し、特定のリクエストでアクセスした場合にのみ評価されるコンテキストプロセッサを使用できます。データベースヒットがすべてのリクエストで発生するという評価を強制しました

典型的なWebアプリのケースは、比較的小さな結果セットの場合です(タイムリーにブラウザーに配信する必要があるため、必要に応じて、ページネーションまたは同様の手法を使用して、データ量を減らします)。したがって、一般的に、標準のQuerySet行動はあなたが望むものです。間違いなくご存じのとおり、キャッシュの利点を得るには、 クエリセットを変数に格納 する必要があります。

イテレータの適切な使用方法:利用可能なメモリを大量に消費する結果を処理します(多数の小さなオブジェクトまたは少数の大きなオブジェクト)。私の経験では、これは重いデータ処理を行う際の管理コマンドによくあります。

30
Steven

私はスティーブンに同意し、観察したいと思います:

  • 「saved_queries = Model.objects.all()だけではなく、より多くの入力が必要です」。はい、ありますが、list(Model.objcts.all())を使用する必要がある主な違いがあります。例を挙げましょう。変数に割り当てられたを配置すると、クエリが実行されてそこに保存されます。つまり、+ 1Mレコードがあるとしましょう。つまり、リストに+ 1Mレコードがあるとします。直後に使用してもしなくてもよいので、スティーブンが言ったようにModel.objects.all()のみを使用することをお勧めします。これは変数に割り当てられているため、実行するまで実行されません変数を呼び出し、DB呼び出しを保存します。

  • Prefetch_related()を使用して、DBへの多くの呼び出しを行わないようにする必要があります。したがって、Django逆引き参照を使用して、時間の節約に役立ちます。

1
Tiago Silva