DjangoのORMのチューニング練習

まず、主として遅い応答への要求分析

タスクの実行)は(time.timeと、断片に機能することにより、各モジュールの印刷実行時間を要求され、ほとんどの場合、性能は主に、すなわち、一つのモジュール20におけるパフォーマンスの問題の80%を消費しますコード上の%

主な理由を見つけた後、この1はモジュールの最適化に注力していきます

第二に、利用django.db.connection.queriesは、要求のSQL実装を表示します

from django.db import connection
...
print(connection.queries)
# [{'sql':--执行的sql语句--, 'time':--sql语句执行的时间--}...]

専用モードのデバッグ=真でconnection.queriesを取得することに注意してください

マルチデータベース

db.connections辞書状物であり、エイリアスデータベース接続を介してこのデータソース接続を得ることができます。そのようなものとしてconnections['my_db_alias']

from django.db import connections
for key in connections:
    print(key)
# 可以打印出所有配置了的数据源别名,django会为每个数据源创建一个connection

ジャンゴ/ DB /ことで初期化中の.py

class DefaultConnectionProxy:
    """
    Proxy for accessing the default DatabaseWrapper object's attributes. If you
    need to access the DatabaseWrapper object itself, use
    connections[DEFAULT_DB_ALIAS] instead.
    """
    def __getattr__(self, item):
        return getattr(connections[DEFAULT_DB_ALIAS], item)

    def __setattr__(self, name, value):
        return setattr(connections[DEFAULT_DB_ALIAS], name, value)

    def __delattr__(self, name):
        return delattr(connections[DEFAULT_DB_ALIAS], name)

    def __eq__(self, other):
        return connections[DEFAULT_DB_ALIAS] == other


connection = DefaultConnectionProxy()

のでDEFAULT_DB_ALIAS='default'、我々は知ることができfrom django.db import connectionGETですconnections['default']

このように、複数のデータベースクエリまたはカーソルの場合には、特定のデータベース接続を接続することによって得ることができます

from django.db import connections
connections['my_db_alias'].queries
cursor = connections['my_db_alias'].cursor()

SQLの実行時間の合計出力

sql_time = 0.0
for q in connections['my_db_alias'].queries:
    sql_time += float(q['time'])
print('sql_time', sql_time)

第三に、様々なアップデートの実行速度の文言

データベースの60ワットへのデータの量

何も更新が実際に更新されていない場合、SQL実行時間が更新され、実際のデータ更新されると、SQL実行時間は、実質的に記録削減されます。

カスタムクエリraw_sqlを使用して1、

cursor = connections['my_db_alias'].cursor()
# 实例化cursor的时间不计入
cursor.execute("update item set result=%s, modified_time=Now() where id=%s", (result, 10000))
print(time()-start)
print(connections['my_db_alias'].queries)
# 0.004s左右,与sql执行时间相同

図2に示すように、更新メソッドを使用してORM

Item.objects.using('my_db_alias').filter(id=10000).update(result=result)
# 0.008s左右,sql执行时间是0.004s

3、object.save()メソッドを使用して

item = Item.objects.using('my_db_alias').filter(id=10000).first()
item.result = result
item.save(using='my_db_alias')
# 0.012s左右,sql执行时间是0.004s

したがって、効率的な更新raw_sqlの実装>更新方法> save()メソッド

第四に、使用することは、データベースクエリを減らすprefetch_related

別の関係を使用してクエリをprefetch_related、すなわち、最初の単離されたIDは、表A、ルックアップテーブルBのID、及びパイソン関連するバッチデータにおける2つのフィルタ条件を満たしています。

ブログは、コメントの複数の関連を持つことができ、2つのテーブルをコメント、ブログ、我々はブログのアプリケーションを持っているがとします

from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=255)
    author = models.CharField(max_length=100)
    content = models.TextField()

class Comment(models.Model):
    author = models.CharField(max_length=100)
    content = models.TextField()
    blog = models.ForeignKey(Blog, on_delete=models.CASCADE, related_name='comments')

の「Djangoのチュートリアル」と呼ばれるすべてのブログでコメントの内容を特定する必要が用意されました。

この例では、我々は、使用するデータベースクエリを減らすprefetch_relatedどのように見ることができます。

def test_prefetch_related():
    blogs = Blog.objects.filter(name="Django教程")
    for blog in blogs:
        comments = Comment.objects.filter(blog_id=blog.id)
        for comment in comments:
            print(comment.content)
    print(len(blogs)) # 34
    print(len(connection.queries)) # 39

指定した名前のブログが34を持っているマッチング、あなたはそれぞれのブログのコメントを見ることができ、それらの両方が、コメントテーブルいったん34回コメント照会テーブルの合計を確認し、効率が非常に低いです。私たちの目標は、ブログのテーブル、クエリ、テーブルを照会するべきである、すなわち、Aコメント必要なデータを取得します

def test_prefetch_related():
    blogs = Blog.objects.filter(name="Django教程").prefetch_related('comments')
    for blog in blogs:
        for comment in blog.comments.all():
            print(comment.content)
    print(len(blogs)) # 34
    print(len(connection.queries)) # 6
    for query in connection.queries:
        print(query)

39によって開始されたSQLの数は6に削減します

具体的:

{'sql': 'SELECT @@SQL_AUTO_IS_NULL', 'time': '0.000'}
{'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', 'time': '0.000'}
{'sql': 'SELECT VERSION()', 'time': '0.000'}
{'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', 'time': '0.000'}

# 找到所有符合过滤条件的博客文章
{'sql': "SELECT `blog`.`id`, `blog`.`name`, `blog`.`author`, `blog`.`content`  FROM `blog` WHERE `blog`.`name` = 'Django教程'", 'time': '0.014'}

# 根据上面找到的博客文章id去找到对应的评论
{'sql': 'SELECT `comment`.`id`, `comment`.`author`, `comment`.`content`, `comment`.`blog_id` FROM `comment` WHERE `comment`.`blog_id` IN (5160, 1307, 2984, 5147, 5148, 3062, 5148, 5161, 2038, 1923, 2103, 3014, 1466, 2321, 5166, 5154, 1980, 3550, 3542, 5167, 2077, 2992, 3209, 5168, 8855, 1163, 368, 174, 3180, 5168, 8865, 2641, 3224, 4094)', 'time': '0.007'}

私たちの目標に沿いました

prefetch_relatedときにキャッシュされたデータは無視されます

キャッシュされたデータは無視されます前に、prefetch_relatedで使用クエリセットの時間に、一度、チェーン操作でデータベース要求を変更するには、注意してください。これは、パフォーマンス上の問題を引き起こし、再要求に対応するデータを取得するためにデータベースをジャンゴを引き起こします。変更要求データベースは、ここに記載)(さまざまなフィルタを指し、(除外)、最終的にSQLコードのような操作を変更します。

(コメント ')prefetch_related)(暗黙blog.comments.allを表し、したがって、すべての()最終的なデータベース要求を変更しないので、再要求データベースをもたらさないであろう。

但し

for comment in blog.comments.filter(author="jack"):

Djangoのデータベースは再要求につながります

唯一のフィールドの一部を削除する必要があります

フィールドデータ内容のブログ記事の量が出て、パフォーマンスに影響を与える可能性があるのではなく、非常に大きくなることがあります。需要はさらにブログやコメントで削除フィールドの一部だけを最適化することができます前に、

blogs = Blog.objects.filter(name="Django教程").only('id').\
    prefetch_related(
        Prefetch('comments', queryset=Comment.objects.only('id', 'content', 'blog_id'))
    )

のみ指定したフィールドのクエリを使用して、プリフェッチは、カスタマイズされたコンテンツprefetch_relatedクエリ(デフォルトオブジェクトを使用queryset=Comment.objects.all()

対応するブログへのコメントパイソン行くにフィールドがcomment.blog_idフィールドは、comment.blog_idプリフェッチ対象に除去されていない場合は無駄、スプライスと一致blog.id必要があるため、注意comment.blog_idフィールドは、削除する必要がありますcomment.blog_idフィールドを見つけるために多くのデータベースクエリ

マルチデータベース

複数のデータベースの場合、データソースは同じprefetch_relatedメインクエリで指定されたデータソースを使用します。

例えば:

blogs = Blog.objects.using('my_db_alias').filter(name="Django教程").only('id').\
    prefetch_related(
        Prefetch('comments', queryset=Comment.objects.only('id', 'content', 'blog_id'))
    )

ブログは、同じデータソーステーブルクエリコメントを使用します。

第五に、データベースへの挿入データbulk_createを利用すること

# 以下代码会发起10次数据库插入:
for i in range(10):
    Comment.objects.create(content=str(i), author="kim", blog_id=1)

# 以下代码只会发起一次数据库插入:
comments = []
for i in range(10):
    comments.append(Comment(content=str(i), author="kim", blog_id=1))
Comment.objects.bulk_create(comments, batch_size=5000)

注意:

  1. bulk_create不会返回ID:あなたが戻って主キーを取得しないINSERT一括

  2. 注意データベース接続タイムアウト:1つのインサートデータの過多は、MySQLがで解消されたエラーにつながる場合。指定BATCH_SIZE = 5000データが> 5000に挿入されたときに、この問題を回避するために、実行データは、SQL INSERTバルク複数に分割され

第六には、データをフェッチ繰り返さないようにしてください

データベースのデータは、効率を向上させる、次回ので、それはデータベースにアクセスせずに再使用され、辞書のメモリをキーとして、IDに保存することができます

おすすめ

転載: www.cnblogs.com/luozx207/p/12163380.html