Django ORMクエリーの最適化


状元は特定の時期にのみクエリーされます.
その時期を知ってこそ最適化できる.
Django ORM機能
  • Lazy Loading
    -ディレイコール
  • Eager Loading
    -即時ロード:N+1 Problem
  • ORMの特徴は、本当に必要なときだけSQLを呼び出すことです.
    言い換えれば、本当に必要な時でなければ
    QuerySetはSQLを呼び出さない
    Djangoがクエリを発行した時刻
    user = User.objects.all()
    a = user.filter(first_name='a')  # 아직 DB에서 데이터를 가져오지않음
    order_a = a.order_by('id') # 아직 DB에서 데이터를 가져오지않음
    b = user.get(id=1)
    parent = b.parent.name  # << 여기서 쿼리를 날림
    では、次の文では、何回クエリーを実行するかを当ててみましょう.
    users = User.objects.all()
    first_user = users[0]
    user_list = list(users)

  • 正解は2回
    最初のプレイヤーとすべてのプレイヤーをインポートするには、User.objects.すべての命令を持ってきたと思います.
    先ほど述べたように、この文章はSQL文を呼び出していません.
    つまり、まだデータを持ってきていません.

  • したがって、最初のユーザをインポートするためにusers[0]と呼びます.
    一度、SQL文が呼び出されたためです.

  • およびすべてのユーザ情報を取得するためのlist(users).
    つまり、SQL文を再度呼び出し、2回のクエリが発生します.
  • N+1問題
    ゲートを回転するたびにクエリごとにsqlが呼び出される問題をN+1問題と呼ぶ.
    (ユーザを一度に呼び出すsql)+(ユーザ数N)=(N+1クエリ)
    この問題を解決する美しい方法を紹介したいです.
    select_related, prefetch_related, Prefetch
    1. select_related
  • オブジェクトは、1対1または複数対1の逆参照とすることができる.
    または外部キーを参照する場合の
  • SQL INNER JOINにて直ちに
  • をロード
    class Child(models.Model):
        name = models.CharField(max_length=20)
    
        def __str__(self):
            return self.name
    
    class Parent(models.Model):
        children = models.ForeignKey("Child", related_name="parent", on_delete=models.CASCADE)
        name = models.CharField(max_length=100)
        def __str__(self):
            return self.name
    以上のモデルは,1:Nからなる関係型モデルである.
    qs = Parent.objects.select_related("children")
    上記のクエリを使用して、最小数(重複クエリを減らす)でデータを取得します.
    これにより、パフォーマンスが向上します.

    上図にselect relatedなしでデータをインポートする例を示します.
    類似または重複したクエリーが重複し続けることがわかります.

    ただし、select relatedを使用すると、最小限のクエリーでデータをロードできることがわかります.
    2. prefetch_related
  • オブジェクトがM:N関係、または逆参照の場合は
  • を使用します.
  • Python追加クエリー
  • qs = children.objects.prefetch_related("parent")
    逆参照として、Childの立場で使う様子.
    prefet relatedを使用するとselect relatedと似ています.
    冗長クエリーを削減し、パフォーマンスを向上させます.
    3. Prefetch
  • 他のクエリセットを制御する方法
  • Prefetch(lookup, queryset=None, to_attr=None)
    Prefetchの原型.
    私たちは常に追加のクエリーセットを制御したいと思っています.
    この場合、prefetch relatedでprefetchを使用することができます.
        from django.db.models import Prefetch
        queryset = post_models.Parent.objects.prefetch_related(
            Prefetch(
                "child",
                queryset=post_models.Child.objects.prefetch_related(
                    Prefetch(
                        "child_of_child",
                        queryset=post_models.ChildofChild.objects.prefetch_related("
                            "child_of_child_of_child"
                        ")
                    )
                )
            ))
    上記のようにデータテープ間の深さ接続形態を制御してもよい.