django公式ドキュメント-データベースアクセスの最適化


データベースアクセスの最適化
Djangoのデータベース・レイヤは、開発者がデータベースの最下位からできるだけ遠ざかるようにするためのさまざまな方法を提供します.このドキュメントには、さまざまなドキュメントへのリンクが収集され、多くのテクニックが追加されています.データベースの使用を最適化しようとすると、この文書をアウトラインとして参照できます.
評価第一
通常のプログラミングの実践として、これは言うまでもない.どのようなクエリーをしているのか、これらのクエリーのコストはいくらですか.django-debug-toolbarのような他のプロジェクトやデータベースを直接監視するツールも必要かもしれません.
速度やメモリ、または両方を最適化するために、必要に応じて最適化される可能性があります.いずれかのターゲットを最適化すると、別のターゲットが損なわれる可能性がありますが、両方が恩恵を受けることもあります.同時にデータベースの最適化を行うよりもPythonコードを最適化するほうがいいかもしれません.これらはあなたのポイントがどこにあるか、バランスポイントがどこにあるかによって決まります.最適化はあなたのアプリケーションとサーバに依存するため、評価する必要があります.
最適化を行う際には、変動のたびに評価を行い、変動が有益であり、コードの可読性の低下による弊害よりもはるかに大きいことを確認しなければならない.以下のすべての提案には、あなたの環境では、一般的な原則が失効したり、有害になったりする可能性があります.
標準データベース技術の使用
...次の内容が含まれます.
  • インデックスを作成します.どのインデックスを作成すべきかを評価した後、インデックスを作成する最初のことをします.djangoを使用することができる.db.models.Field.db_indexでインデックスを作成します.
  • 適切なフィールドタイプを選択します.

  • 本文では、上記の明らかなことをすべてやったと仮定します.以下では、必要な仕事をした場合にDjangoをどのように使用するかに注目します.また、本明細書では、一般的なターゲットのキャッシュなどの重量レベルの操作についても触れません.
    クエリセットの理解/
    クエリー・セットを理解することは非常に重要であり、クエリー・セットを理解してこそ、簡明なコードで優れたパフォーマンスを得ることができます.特に以下の点を把握します.
    クエリセットの実行を理解する.
    パフォーマンスの問題を回避するには、次の点を理解する必要があります.
  • クエリーセットは不活性です.
  • クエリーセットがいつ実行されたか.
  • データがメモリにどのように格納されているか.

  • キャッシュ属性の理解/
    クエリーセット全体をキャッシュするのと同様に、ORMオブジェクトのプロパティにもキャッシュがあります.通常、呼び出せないプロパティはキャッシュされます.たとえば、ブログモデルの例を想定します.
    >>> entry = Entry.objects.get(id=1)
    >>> entry.blog   #    Blog   
    >>> entry.blog   #      ,      
    

    しかし、通常、呼び出すことができる属性は、毎回データベース・クエリーを開始します.
    >>> entry = Entry.objects.get(id=1)
    >>> entry.authors.all()   #     
    >>> entry.authors.all()   #       
    

    テンプレートコードを読むときは、カッコは使用できませんが、呼び出し可能なプロパティが自動的に呼び出されるので注意してください.
    カスタム属性がキャッシュを呼び出すかどうかは自分で決めるので、カスタム属性にも気をつけてください.
    withテンプレートタグの使用
    クエリーセットのキャッシュ動作を使用するには、withテンプレートタグを使用する必要があります.
    iterator()の使用
    オブジェクトが大量にある場合、クエリー・セットのキャッシュ・動作に大量のメモリが消費される可能性があります.この場合、iterator()を使用すると役立ちます.
    Pythonでデータベース操作を実行しないで、できるだけデータベースで実現してください.
    例:
  • は、多くの基本レベルで、フィルタリングおよび除外を使用してデータベース内でフィルタリングされる.
  • F()オブジェクトクエリー式を使用して、同じモデル内の他のフィールドをフィルタします.
  • は、統計を使用してデータベース内で統計を行う.

  • 必要なSQLを生成するのに十分でない場合は、次のことができます.
    QuerySetを使用extra()¶
    軽くはありませんが、もっと力のある方法はextra()を使うことです.クエリーにSQLを明示的に追加できます.まだ足りない場合は、次のことができます.
    元のSQLの使用
    自分で書いたSQL文を使用して、データを取得したり、モデルを生成したりすることができます.元のSQLを使用する前にdjangoを使用してください.db.connection.queriesはDjangoがどのようにSQLを生成したかを表示します.
    君が望むものを一度に手に入れる.
    バッチでデータベースから必要なデータを取得するのは、すべてのデータを一度に取得するよりも効率が低いことが多い.1つのサイクルでデータを取得する場合、効率が低下すると、クエリの数が急増します.次のようにします.
    QuerySetを使用select_related()¶
    QuerySet.を徹底的に理解するselect_related()は、次の場所で使用されます.
  • ビューコード、
  • は、適切なマネージャおよびデフォルトマネージャにあります.いつ使用したか、またはマネージャが使用されていないかを注意して確認してください.時には迷いやすい問題で、溝の中で船が転覆することに注意してください.

  • いらないものを手に入れてはいけない.
    QuerySetを使用values()とvalues_list()¶
    値を含むdictまたはlistが1つしか必要なく、ORMモデルオブジェクトが必要ない場合はvalues()を正しく使用します.この方法は、あなたが提供した辞書が同じ属性を提供しているため、テンプレートコードでモデルオブジェクトの代わりに使用することができます.
    QuerySetを使用defer()とonly()/
    データベースに不要な(またはほとんど必要ない)カラムがある場合はdefer()とonly()を使用して、カラムをロードします.これらのカラムを使用しないと、ORMは個別のクエリでしか取得できません.これはよくありません.
    また、遅延フィールドを使用してモデルを構築する場合、Django内部には一定のコストがかかることがわかります.したがって、評価なしに遅延フィールドを乱用しないでください.結局、1行のデータのために、最後にわずかな列でも、データベースはディスクからほとんどの非テキストまたは非文字タイプのデータを読み込まなければなりません.defer()とonly()メソッドは、大量のテキストをPythonオブジェクトに変換するのはリソースを消費するため、大量のテキストをロードしない場合に最も適しています.しかし、まず評価してから最適化しなければならない.
    QuerySetを使用count()¶
    ...カウントしたいだけならQuerySet.count()はlen(queryset)よりいいです.
    QuerySetを使用exists()¶
    ...少なくとも1つの結果があるかどうかを発見したいならQuerySet.exists()はif querysetより良いです.
    ただし:
    count()とexists()を過度に使用しないでください.
    セットの他のデータをクエリーする必要がある場合は、count()とexists()を使用しないでください.
    例えば、body属性を持ち、Userに対応する多対多関係を持つ電子メールモデルがあると仮定する.次のテンプレートコードが最適です.
    {% if display_inbox %}
      {% with emails=user.emails.all %}
        {% if emails %}
          <p>You have {{ emails|length }} email(s)</p>
          {% for email in emails %}
            <p>{{ email.body }}</p>
          {% endfor %}
        {% else %}
          <p>No messages today.</p>
        {% endif %}
      {% endwith %}
    {% endif %}
    

    以上のコードが最も優れている理由は以下のとおりです.
  • クエリセットが不活性であるため、'display_inbox'が偽であれば、データベースクエリーは実行されません.
  • withを使用するとuser.emails.allは、後で使用するために変数に格納されます.すなわち、後で繰り返し使用するためにキャッシュされます.
  • {%if emails%}この行のコードはQuerySet._を開始します.nonzero__()呼び出され、userを開始する.emails.all()クエリはデータベースで実行され、少なくとも最初のローを1つのORMオブジェクトに変換します.結果がある場合はTrueを返し、そうでない場合はFalseを返します.
  • は{{emails|length}}を用いてQuerySet._を呼び出す.len__()は、クエリーを行わずにキャッシュを呼び出します.
  • forループはキャッシュに繰り返し作用する.

  • 要するに、上記のコードは1回しかクエリーをしないか、しないだけです.コードの中で最も重要な最適化はwithタグを使用することです.どこでもQuerySetを使えばexists()またはQuerySet.count()は追加のクエリーをもたらします.
    QuerySetを使用update()とdelete()
    大量のオブジェクトを割り当てて保存する場合は、1つずつ実行するよりも、大量のSQL UPDATE文QuerySetを使用します.update() .同様に、可能であれば一括削除を使用する必要があります.
    これらの一括操作文は、各インスタンスのsave()またはdelete()メソッドを呼び出すことはできません.これは、一般的なデータベース・オブジェクト信号によって駆動されるものを含め、これらのメソッドに追加されたカスタム動作が実行されないことを意味します.
    外部キーを直接使用する値
    外部キーの値が1つしか必要ない場合は、リレーショナル・オブジェクト全体を取得してプライマリ・キーを取得するのではなく、取得したオブジェクトの外部キー値を直接使用します.たとえば、次のようにします.
    entry.blog_id
    

    そうすべきではありません.
    entry.blog.id