Django - QuerySet
Lazy Loading(遅延ロード)とは?
ORMがSQLを呼び出す特徴のうち、QuerySetを呼び出す方法は
실제로 필요할 때 호출한다.
の特徴を有する.たとえば、商品情報を含む「製品」(Products)テーブルを読み込む場合は、次のコードに従って作成します.
products = Product.objects.all()
かっこで終わるので、当然Productクラスを実行するときに、すべての関数が実行する値が製品に含まれていると考えられます.ただし、「在庫」(ORM)では、単純な宣言ではSQLを呼び出すことはできません.コンテンツに関するSQLを呼び出すには、次の手順に従います.
products = Product.objects.all()
list(product)
#or
for product in products:
print(product.name)
ドラム公式ドキュメントでは、次のようにQueryの動作状況をまとめています.ドラム公式文書
すなわち、SQL文は、QuerySet(複数のオブジェクトを簡単に集約)を呼び出すために、スライス、Iteration(ループ可能)repr()、len()などの一定の方法で実行されなければならない.
遅延負荷の問題(効率低下,N+1の問題)
では、これらの特徴によって発生する状況は何でしょうか.コールを続けます.どういう意味なのか、上に見たコードを例に挙げてみましょう.
#Product에는 현재 2,940개의 row가 저장되어 있다.
products = Product.objects.all()
products[0].name
products[1].name
...
products[2939].name
>>>
상품1
상품2
상품3
...
...
상품2939
상품2940
上記のコードを使用して実行すると、SQL文は引き続きDBに送信されます.商品1のqeuryと商品2のqeuryもう一度仮定しようもし商品が店舗を参照したら?
class Store(models.Model):
name = models.CharField()
class Product(models.Model):
name = models.CharField()
store = models.ForeignKey(Store, on_delete=models.CASCADE)
もちろん、ショップと商品はM:N関係ですが、便宜上、商品が店を噛んでいると仮定します.商品に対応するファミリーを見るためにORMを使って検索すると以下のように記述できます.
products = Product.objects.all()
stores = []
for product in products:
stores.append(
{
'id' : product.id,
'name' : product.name.
'store' : product.store.name
}
)
上記のコードを実行すると、商品のrow個数で照会されますか?違うはずです.商品の照会と店舗への照会は、少なくとも2倍の照会をもたらします.これはN + 1 problem
です.ハードウェアのパフォーマンスが低いか、データベースが耐えられない(計算量が非常に大きい)場合は、正しい値を返して中断することができず、最悪の場合、データベースが拡張される可能性があります.
Caching
キャッシュは、クエリーにロードされたデータをキャッシュに格納し、複数回の呼び出しを回避するのに役立ちます.上のコードでfor文、すなわち反復化が行われると、キャッシュはクエリ文によって呼び出された内容を格納します.
すなわち、
products
のcache
にロードされた情報が格納される.その後、コンテンツが再実行されると、キャッシュに格納されているコンテンツは、他の呼び出しを行わずに呼び出されます.products = Product.objects.all()
for product in products:
product.name
#1번 호출함
print(products[0].name)
>>>
상품 1
#cache 에 저장된 내용을 호출했기에 새로운 query를 호출하지 않음
ドラムにはキャッシュを使う方法がたくさんありますが、今では内容が膨大になっているので、単独で議論しましょう.(きっと!)Eager-Loding(即時ロード)(選択に関連、プリフェッチに関連)
インスタント・ロードは、遅延ロードとは逆の概念であり、遅延評価、実際の評価時にクエリーを発行するのではなく、すべてのものを一度にもたらす方法です.
本論文では,
select_related
とprefetch_related
という関数を用いてEager Loadingを実現した.用法は似ているが違いがあり、違いは以下の通りである.
select relatedの使い方は以下の通りです.
select_related(field명-변수)
#store 필드까지 한번에 쿼리문으로 불러온다.
products = Product.objects.all().select_related('store')
stores = []
for product in products:
stores.append(
{
'id' : product.id,
'name' : product.name.
'store' : product.store.name
}
)
上記のコードのように使用すると、クエリ文はDBに1回しか到達しません.select relatedではなくプリフェッチrelatedを使用してコードを記述し、逆参照関係を実行します.
stores = Store.objects.all().prefetch_related('product_set')
store = []
for store in stores:
products = [product.name for product in stores.product_set.all()]
store.append(
{
'id' : store.id,
'name' : store.name,
'products' : products
}
)
prefetch relatedを使用すると、少なくとも2回のクエリがDBに渡されます.選択したクエリーと、コンテンツから参照に再入力するクエリーが実行されます.
この内容で
상품99
というフィルターを使ったらどうなるでしょうか?stores = Store.objects.all().prefetch_related('product_set')
store = []
for store in stores:
all_products = [product.name for product in stores.product_set.all()]
filtered_product = [product.name for product in stores.product_set.filter(name = '상품99')
store.append(
{
'id' : store.id,
'name' : store.name,
'all_products' : all_products,
'filtered_product' : filtered_product
}
)
上記のコードに示すように、荘子はクエリ文を2回以上呼び出して상품99
を検索する.以上に設定したプリフェッチ関連コマンドはproduct setであるためです.all()にしか掛けていないので.
本明細書で設定したオブジェクトのprefetch relatedを使用して、prefetch relatedと一致しない問題を解決します.ちょっと紛らわしいですが、次のコードを見れば理解できます.
stores = Store.objects.prefetch_related(
Prefetch(
'product_set',
stores = Product.objects.all(),
to_attr='all_products'
),
Prefetch(
'product_set',
stores = Product.objects.filter(name='상품99'),
to_attr='filtered_product'
)
)
store = []
for store in stores:
all_products = [product.name for product in stores.all_products]
filtered_product = [product.name for product in stores.filtered_product)
store.append(
{
'id' : store.id,
'name' : store.name,
'all_products' : all_products,
'filtered_product' : filtered_product
}
)
to_attr
が任意の属性を指定していることがわかります.n/a.結論
最終的な結論は、ORMを使用する場合、クエリー最適化環境を構成することです.
つまり、キャッシュを使用して、Eager-Loadingを考慮し、フレームワークを理解します.
Reference
この問題について(Django - QuerySet), 我々は、より多くの情報をここで見つけました https://velog.io/@holyja/Django-selectrelatedテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol