59

私はapacheとmod_wsgiとPostgreSQL(すべて同じホスト上)でdjangoを使用しており、多くの単純な動的ページ要求(数百/秒)を処理する必要があります。ボトルネックは、djangoに永続的なデータベース接続がなく、リクエストごとに再接続することです(5ミリ秒近くかかります)。ベンチマークを実行している間、持続的接続を使用すると500 r / s近くを処理できますが、使用しない場合は50 r/sしか取得できません。

誰かアドバイスはありますか?永続的な接続を使用するように、またはPythonからDBへの接続を高速化するようにDjangoを変更するにはどうすればよいですか?

4

7 に答える 7

35

Django 1.6は持続的接続のサポートを追加しました(最新の安定したDjangoのドキュメントへのリンク)

持続的接続は、各要求でデータベースへの接続を再確立するオーバーヘッドを回避します。これらは、接続の最大存続期間を定義するCONN_MAX_AGEパラメーターによって制御されます。データベースごとに個別に設定できます。

于 2013-10-17T22:13:17.980 に答える
26

PgBouncerを試してください-PostgreSQL用の軽量接続プールです。特徴:

  • 接続を回転させるときの残虐行為のいくつかのレベル:
    • セッションプーリング
    • トランザクションプーリング
    • ステートメントプーリング
  • 低メモリ要件(デフォルトでは接続ごとに2k)。
于 2009-11-08T22:15:33.920 に答える
20

Djangoトランクで、django/db/__init__.py次の行を編集してコメントアウトします。

signals.request_finished.connect(close_connection)

このシグナルハンドラにより、リクエストのたびにデータベースから切断されます。これを行うことのすべての副作用がどうなるかはわかりませんが、すべての要求の後に新しい接続を開始することは意味がありません。お気づきのように、パフォーマンスが低下します。

私は今これを使用していますが、何かが壊れているかどうかを確認するための完全なテストセットを実行していません。

なぜこれに新しいバックエンドや特別な接続プール、またはその他の複雑なソリューションが必要だと誰もが考えるのかわかりません。これは非常に単純に思えますが、そもそもこれを実行させたいくつかのあいまいな落とし穴があることは間違いありません。これは、より賢明に対処する必要があります。お気づきのように、すべてのリクエストの5ミリ秒のオーバーヘッドは、高性能サービスにとってはかなりの量です。(150msかかります-理由はまだわかりません。)

編集:もう1つの必要な変更はdjango / Middleware/transaction.pyにあります。2つのtransaction.is_dirty()テストを削除し、常にcommit()またはrollback()を呼び出します。それ以外の場合は、データベースから読み取るだけの場合はトランザクションをコミットしません。これにより、ロックが開いたままになり、閉じる必要があります。

于 2009-07-23T23:55:40.023 に答える
16

sqlalchemyプーリングを介してMySQLとPostgreSQLの接続プールを実装する小さなDjangoパッチを作成しました。

これは、http: //grandcapital.net/の制作で長期間完全に機能します。

パッチは、トピックを少しグーグルした後に書かれました。

于 2011-07-15T19:26:17.093 に答える
4

免責事項:私はこれを試していません。

カスタムデータベースのバックエンドを実装する必要があると思います。接続プールを使用してデータベースバックエンドを実装する方法を示すいくつかの例がWeb上にあります。

接続がプールに戻されるときにネットワーク接続が開いたままになるため、接続プールを使用することはおそらくあなたの場合に良い解決策でしょう。

  • この投稿は、Djangoにパッチを適用することでこれを実現しています(コメントの1つは、コアdjangoコードの外部にカスタムバックエンドを実装する方が良いと指摘しています)
  • この投稿は、カスタムデータベースバックエンドの実装です

どちらの投稿もMySQLを使用しています-おそらくPostgresqlで同様の手法を使用できます。

編集:

于 2009-07-14T14:05:49.020 に答える
0

グローバル変数を使用して持続的接続を実装する小さなカスタムpsycopg2バックエンドを作成しました。これにより、1秒あたりのリクエスト数を350から1600に改善することができました(選択がほとんどない非常に単純なページで)base.py任意のディレクトリ(postgresql_psycopg2_persistentなど)で呼び出され、設定で設定されたファイルに保存するだけです

DATABASE_ENGINEからprojectname.postgresql_psycopg2_persistent

ノート!!!コードはスレッドセーフではありません-予期しない結果が発生するため、Pythonスレッドでは使用できません。mod_wsgiの場合は、threads=1でpreforkデーモンモードを使用してください。


# Custom DB backend postgresql_psycopg2 based
# implements persistent database connection using global variable

from django.db.backends.postgresql_psycopg2.base import DatabaseError, DatabaseWrapper as BaseDatabaseWrapper, \
    IntegrityError
from psycopg2 import OperationalError

connection = None

class DatabaseWrapper(BaseDatabaseWrapper):
    def _cursor(self, *args, **kwargs):
        global connection
        if connection is not None and self.connection is None:
            try: # Check if connection is alive
                connection.cursor().execute('SELECT 1')
            except OperationalError: # The connection is not working, need reconnect
                connection = None
            else:
                self.connection = connection
        cursor = super(DatabaseWrapper, self)._cursor(*args, **kwargs)
        if connection is None and self.connection is not None:
            connection = self.connection
        return cursor

    def close(self):
        if self.connection is not None:
            self.connection.commit()
            self.connection = None

または、これはスレッドセーフなスレッドですが、Pythonスレッドは複数のコアを使用しないため、前のスレッドのようにパフォーマンスが向上することはありません。これはマルチプロセスのものでも使用できます。

# Custom DB backend postgresql_psycopg2 based
# implements persistent database connection using thread local storage
from threading import local

from django.db.backends.postgresql_psycopg2.base import DatabaseError, \
    DatabaseWrapper as BaseDatabaseWrapper, IntegrityError
from psycopg2 import OperationalError

threadlocal = local()

class DatabaseWrapper(BaseDatabaseWrapper):
    def _cursor(self, *args, **kwargs):
        if hasattr(threadlocal, 'connection') and threadlocal.connection is \
            not None and self.connection is None:
            try: # Check if connection is alive
                threadlocal.connection.cursor().execute('SELECT 1')
            except OperationalError: # The connection is not working, need reconnect
                threadlocal.connection = None
            else:
                self.connection = threadlocal.connection
        cursor = super(DatabaseWrapper, self)._cursor(*args, **kwargs)
        if (not hasattr(threadlocal, 'connection') or threadlocal.connection \
             is None) and self.connection is not None:
            threadlocal.connection = self.connection
        return cursor

    def close(self):
        if self.connection is not None:
            self.connection.commit()
            self.connection = None
于 2009-08-29T11:32:36.633 に答える
0

これはdjango接続プールのパッケージです: django-db-connection-pool

pip install django-db-connection-pool

SQLAlchemyのプール作成に渡す追加のオプションを提供できます。キーの名前はPOOL_OPTIONSです。

DATABASES = {
    'default': {
        ...
        'POOL_OPTIONS' : {
            'POOL_SIZE': 10,
            'MAX_OVERFLOW': 10
        }
        ...
     }
 }
于 2021-04-18T18:55:09.147 に答える