私はDjango= Apacheとmod_wsgiとPostgreSQL(すべて同じホスト上で)を使用していますが、多くの単純な動的ページ要求(1秒あたり数百)を処理する必要があります。ボトルネックは、Django永続的なデータベース接続を持たず、各リクエストで再接続します(5ms近くかかります)。ベンチマークを行っている間に、永続的な接続で500 r/sではなく、50 r/sしか得られません。
誰にもアドバイスがありますか?修正方法Django=持続的接続を使用しますか?またはpythonからDBへの接続を高速化します
前もって感謝します。
Django1.6が追加されました 永続的な接続のサポート(Django 1.9)のドキュメントへのリンク :
永続的な接続により、各リクエストでデータベースへの接続を再確立するオーバーヘッドが回避されます。接続の最大存続期間を定義するCONN_MAX_AGEパラメーターによって制御されます。データベースごとに個別に設定できます。
PgBouncer -PostgreSQL用の軽量接続プーラーを試してください。特徴:
Django trunk、edit Django/db/__init__.py
そして行をコメントアウトします:
signals.request_finished.connect(close_connection)
このシグナルハンドラにより、リクエストのたびにデータベースから切断されます。これを行うことによる副作用がすべてどうなるかはわかりませんが、リクエストごとに新しい接続を開始しても意味がありません。あなたが気づいたように、それはパフォーマンスを破壊します。
私は今これを使用していますが、何かが壊れているかどうかを確認するためのテストの完全なセットを行っていません。
誰もがこれに新しいバックエンドや特別な接続プーラー、その他の複雑なソリューションが必要だと考える理由がわかりません。これは非常に簡単に思えますが、そもそもこれを行うようになったあいまいな落とし穴があることは疑いありません。お気づきのように、高性能サービスの場合、リクエストごとに5ミリ秒のオーバーヘッドがかなりあります。 (それは私を取ります150ms-私はまだ理由を理解していません。)
編集:別の必要な変更はDjango/middleware/transaction.pyにあります。 2つのtransaction.is_dirty()テストを削除し、常にcommit()またはrollback()を呼び出します。そうでない場合、データベースから読み取るだけの場合、トランザクションはコミットされず、閉じられるロックが開いたままになります。
Sqlalchemyプーリングを介してMySQLとPostgreSQLの接続プーリングを実装する小さな Djangoパッチ を作成しました。
これは、長期間の http://grandcapital.net/ の生成で完全に機能します。
パッチは、トピックを少しグーグルした後に書かれました。
免責事項:私はこれを試していません。
カスタムデータベースバックエンドを実装する必要があると思います。 Webには、接続プーリングを使用してデータベースバックエンドを実装する方法を示すいくつかの例があります。
接続がプールに戻されたときにネットワーク接続が開いたままになるので、おそらく接続プールを使用するのが適切なソリューションです。
どちらの投稿もMySQLを使用しています。おそらく、Postgresqlで同様の手法を使用できます。
編集:
グローバル変数を使用して永続的な接続を実装する小さなカスタムpsycopg2バックエンドを作成しました。これにより、1秒あたりのリクエスト数を350から1600に改善することができました(選択の少ない非常にシンプルなページで)任意のディレクトリ(たとえばpostgresql_psycopg2_persistent)のbase.py
というファイルに保存して
DATABASE_ENGINEからprojectname.postgresql_psycopg2_persistentへ
注!!!コードはスレッドセーフではありません-予期しない結果のため、pythonスレッドで使用することはできません。mod_wsgiの場合、thread = 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スレッドは複数のコアを使用しないため、以前のようなパフォーマンスの向上は得られません。これをマルチプロセス1で使用できます。も。
# 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