Djangoパフォーマンスの最適化

8922 ワード

DjangoはPythonベースのウェブサイト開発フレームワークであり、重要な特徴はBattery Incluudedであり、簡単に言えば従来の開発に必要なすべてのものを含み、完全なORMモデル、ミドルウェア、セッション処理、テンプレート言語、ルーティングマッピング、管理者サイトなど、開発者の開発体験を大幅に向上させた.今日お話しするのは、Django ORMのパフォーマンス最適化という内容です.
一、データベース接続
django1.6以降はデータベース永続化接続が組み込まれており、デフォルトでは永続化接続は使用されず、各Webサイトのリクエストはデータベースに接続されます.データベースがローカルにない場合は、ネットワーク速度が速いにもかかわらず、20~75 msかかります.
永続化接続を設定するには、CONNを追加するだけです.MAX_AGEパラメータはあなたのデータベース設定にあります.
DATABASES = {
    ‘default’: {
        ‘ENGINE’: ‘django.db.backends.postgresql_psycopg2’,
        ‘NAME’: ‘whoohoodb’,
        ‘CONN_MAX_AGE’: 600,
    }
}

このような設定により、永続化された接続は毎回10分間生存し、メモリの漏洩やシート状の接続の問題を低減するのに役立ちます.あなたももっと長い時間を設定することができますが、私は1時間以上設定しないでください.これはあまり効果が得られないからです.djangoのヘルプドキュメントから詳細を入手できます.https://docs.djangoproject.com/en/1.8/ref/databases/#persistent-connections.
 
二、索引
よく使用するフィルタフィールドでは、クエリーのパフォーマンスを向上させるためにインデックスを追加することを考慮します.もちろんインデックスの追加ポリシーは複雑な命題ですが、ほとんどの場合、それを加えるのは間違いありません.
 
三、select_related()とprefetch_related()
実際の開発では,モデル間に複雑な関連関係がしばしば存在する.データ量が多い場合、デフォルトのクエリーは潜在的なパフォーマンスの問題に直面する可能性があります.今日はDjango ORMのクエリー最適化についてお話しします.
まず、次の点を明確にする必要があります.
Querysetは不活性評価です.
Djangoでは、すべてのQuerysetは不活性であり、クエリーセットを作成するときにデータベースと対話しないことを意味します.したがって,クエリセットをカスケードするfilterなどの操作を行うことができ,QuerysetのコンテンツにアクセスするときだけDjangoがデータベースへのアクセスを本格的に行うことができる.一方、多頻度で複雑なデータベース・クエリーは、パフォーマンスの問題の最大の根源となります.
説明を簡単にするために、以下のモデルを定義します.
1 class A(models.Model):
2     foo = models.IntegerFiled()
3 
4 class B(models.Model):
5     a = models.ForeignKey(A, related_name='bs')

 
関連関係では、外部キーのクエリは依然として不活性です.外部キーで関連オブジェクトを取得すると、実際にはデフォルトで関連オブジェクトのIDが取得されます.この場合、実際の関連オブジェクトを必要としないIDのみが必要なシーンに適用されます.これはDjangoのドキュメントで説明されています.
If you only need a foreign key value, use the foreign key value that is already on the object you’ve got, rather than getting the whole related object and taking its primary key.
しかし,実際の開発では,外部キーオブジェクトの他の属性にアクセスする必要があることが多い.デフォルトのクエリーで値を取得すると、データベース・クエリーが複数回発生し、効率が予想されます.
select_relatedとprefetch_relatedは、この問題を解決するために、オブジェクトの集合をクエリーするときに、指定した外部キーオブジェクトもクエリーを完全にロードし、後続の重複クエリーを回避することができます.
したがって、Bの外部キー関係オブジェクトAの情報は、以下のようにして取得することができる.
1 b = B.objects.select_related('a').all()
2 
3 for temp_b in b:
4     print temp_b.a.foo

以上のように、実際にはデータベース・クエリーが1回しかトリガーされず、クエリーのパフォーマンスが大幅に向上します.
prefetch_related効果とselect_relatedは似ていますが、使用するシーンは異なります.
    1,select_relatedは、外部キーとマルチペアの関係クエリーに適用されます.
    2,prefetch_relatedは、1対または複数対のクエリーに適用されます.
 
四、iterator()
大量のデータを一度に検出する必要がある場合、Djangoはデフォルトでメモリにデータをキャッシュします.これにより、メモリの消費量が大きくなります.
ここではiterator()をお勧めします.結果をディスクやswapにキャッシュし、使用するときにもう1つ取り出し、メモリを節約して使用します.
 
五、できるだけデータベースの検索回数を減らす
単一のアクション(同じページなど)でデータベースに複数回接続する必要がある場合は、必要なデータを一度にすべて取り出し、データベースに接続する回数を減らすことが望ましい.
このようなニーズはQuerySet.を推奨する.select_related()とprefetch_related();
逆に、あなたが必要としないものを取り出さないでください.テンプレートtemplatesでは、すべてではなくエンティティのいくつかのフィールドしか必要としません.このときQuerySet.values()とvalues_List()は、必要なフィールドだけを取り、辞書dictとリストlistタイプのものを返し、テンプレートで十分に使えます.これにより、メモリ損失を減らし、パフォーマンスを向上させます.
同じくQuerySet.defer()とonly()はパフォーマンスの向上にも役立ち、1つのエンティティに多くのフィールドがある可能性があります.一部のフィールドにはブログの本文、多くの文字構成、Djangoがエンティティを取得する場合(エンティティを取り出す過程でpythonクラス型変換作業を行う場合)、大量のメタデータフィールドの処理を遅らせ、必要なキーフィールドだけを処理することができます.この時QuerySet.defer()は役に立ち、関数に遅延処理が必要なフィールドを入力すればよい.only()とdefer()は逆の機能です.
QuerySetを使用count()はlen(queryset)の代わりに,この2つの処理の結果は同じであるが,前者は性能が優れている.同理判定記録が存在する場合、QuerySet.exists()はif querysetよりずっと強いです.
 
六、主従を配置し、読み書きを分離する
プライマリ・スレーブ・データベースの構成を追加するには、次の手順に従います.
DATABASES = {
      'default': {
          'ENGINE': 'django.db.backends.mysql',
          'NAME': 'dailyfresh',
          'HOST': '192.168.243.193', # MySQL     ( )
          'PORT': '3306',
          'USER': 'root',
          'PASSWORD': 'mysql',
      },
      'slave': {
              'ENGINE': 'django.db.backends.mysql',
              'NAME': 'dailyfresh',
              'HOST': '192.168.243.189', # MySQL     ( )
              'PORT': '3306',
              'USER': 'root',
              'PASSWORD': 'mysql',
          }
  }

使用する場合はusing()で対応するライブラリ名を指定すればよい.