Python でのデータベース接続プーリングの最適なソリューションは何ですか?
-
01-07-2019 - |
質問
私は、いかなる種類のフレームワーク内でも実行されないサーバー側プロセスであるプロジェクトの非常に特殊な要件を満たすために、いくつかのカスタム DAO のようなクラスを開発しました。
このソリューションは、新しいリクエストが行われるたびに MySQLdb.connect 経由で新しい接続を開くことを除けば、うまく機能します。
これをPythonでの接続プーリングの使用に切り替えるための最良の「ドロップイン」ソリューションは何ですか?Java 用の Commons DBCP ソリューションのようなものを想像しています。
プロセスは長時間実行されており、リクエストを行う必要があるスレッドが多数ありますが、すべてを同時に行う必要はありません...具体的には、結果の一部を短時間で書き出す前に、かなりの作業を行います。
編集して以下を追加しました:さらに検索した後、見つけました anitpool.py これはまともなように見えますが、私はPythonに比較的慣れていないので、より明白で、より慣用的で、より良い解決策を見逃していないことを確認したいだけだと思います。
解決
IMO によれば、「より明白で、より慣用的で、より良い解決策」は、DAO のようなクラスを発明するのではなく、既存の ORM を使用することです。
ORM は「生の」SQL 接続よりも人気があるように思えます。なぜ?パイソンだから は OO、および SQL 行からオブジェクトへのマッピング は 絶対に必要不可欠です。Python オブジェクトにマップされない SQL 行を扱うケースはそれほど多くありません。
私はそう思います SQLアルケミー または SQLオブジェクト (および関連する接続プーリング) は、より慣用的な Python ソリューションです。
純粋な SQL (オブジェクト マッピングなし) は、接続プーリングの恩恵を受ける複雑で長時間実行されるプロセスにはあまり普及していないため、別個の機能としてのプーリングはあまり一般的ではありません。はい、純粋な SQL は 使用されますが、プーリングが役に立たない、より単純なアプリケーションやより制御されたアプリケーションで常に使用されます。
次の 2 つの選択肢があると思います。
- SQLAlchemy または SQLObject を使用するようにクラスを修正してください。これは最初は苦痛に思えますが (すべての作業が無駄になりました)、すべての設計と思考を活用できるはずです。これは、広く使用されている ORM およびプーリング ソリューションを採用するための演習にすぎません。
- 概説したアルゴリズム (循環する接続の単純なセットまたはリスト) を使用して、独自の単純な接続プールを作成します。
他のヒント
MySQLで?
接続プーリングは気にしないほうがいいと思います。これらはトラブルの原因となることが多く、MySQL では期待するようなパフォーマンス上の利点は得られません。この分野では、接続プーリングの利点について多くのベスト プラクティスや教科書的な言葉が飛び交っているため、この道をたどるには政治的に多大な努力が必要になる可能性があります。
接続プールは単に Web 時代以降のステートレス アプリケーション (例:HTTP プロトコル)と、ステートフルで長期存続するバッチ処理アプリケーションの Web 以前の時代。Web 以前のデータベースでは接続が非常に高価だったので (接続の確立にかかる時間をあまり気にする人は誰もいなかったため)、Web 以降のアプリケーションでは、すべてのヒットでこのような巨大な処理オーバーヘッドが発生しないように、この接続プール スキームが考案されました。 RDBMS上で。
MySQL は Web 時代の RDBMS に近いため、接続は非常に軽量で高速です。私は、MySQL 用の接続プールをまったく使用しない、大容量の Web アプリケーションを多数作成してきました。
これは、克服すべき政治的障害がない限り、複雑な問題を回避することで利益を得ることができるかもしれません。
接続クラスをラップします。
作成する接続の数に制限を設定します。未使用の接続を返します。近くでインターセプトして接続を解放します。
アップデート:dbpool.py に次のような内容を記述しました。
import sqlalchemy.pool as pool
import MySQLdb as mysql
mysql = pool.manage(mysql)
古いスレッドですが、汎用プーリング (接続または高価なオブジェクト) には次のようなものを使用します。
def pool(ctor, limit=None):
local_pool = multiprocessing.Queue()
n = multiprocesing.Value('i', 0)
@contextlib.contextmanager
def pooled(ctor=ctor, lpool=local_pool, n=n):
# block iff at limit
try: i = lpool.get(limit and n.value >= limit)
except multiprocessing.queues.Empty:
n.value += 1
i = ctor()
yield i
lpool.put(i)
return pooled
これは遅延して構築され、オプションの制限があり、私が考えることができるあらゆるユースケースに一般化する必要があります。もちろん、これは、あらゆるリソースのプールが本当に必要であることを前提としていますが、多くの最新の SQL のようなものでは必要ないかもしれません。使用法:
# in main:
my_pool = pool(lambda: do_something())
# in thread:
with my_pool() as my_obj:
my_obj.do_something()
これは、ctor が作成するオブジェクトには、必要に応じて適切なデストラクターがあることを前提としています (サーバーによっては、明示的に閉じられない限り、接続オブジェクトを強制終了しないものもあります)。
ちょうど同じようなものを探していたところです。
見つけました pysqlプール そしてその sqlalchemy プールモジュール
アプリがマルチスレッドの使用を開始することに決めた場合、独自の接続プールを作成するのは悪い考えです。マルチスレッド アプリケーション用の接続プールの作成は、シングルスレッド アプリケーション用の接続プールの作成よりもはるかに複雑です。その場合は、PySQLPool などを使用できます。
パフォーマンスを求める場合、ORM を使用するのも悪い考えです。
多くの選択、挿入、更新、削除を同時に処理する必要がある巨大/重いデータベースを扱う場合、パフォーマンスが必要になります。つまり、ルックアップを最適化するためにカスタムSQLが書き込まれる必要がありますロックタイム。ORM では通常、そのような柔軟性はありません。
つまり、基本的には、独自の接続プールを作成して ORM を使用できますが、これは、今説明したものが何も必要ないと確信できる場合に限ります。
古いスレッドに返信しますが、最後に確認したとき、MySQL はドライバーの一部として接続プーリングを提供しています。
それらは次の場所で確認できます。
https://dev.mysql.com/doc/connector-python/en/connector-python-connection-pooling.html
TFAから、接続プールを明示的に開きたいと仮定します(OPが述べたように):
dbconfig = { "database": "test", "user":"joe" }
cnxpool = mysql.connector.pooling.MySQLConnectionPool(pool_name = "mypool",pool_size = 3, **dbconfig)
このプールは、get_connection() 関数を通じてプールからリクエストすることによってアクセスされます。
cnx1 = cnxpool.get_connection()
cnx2 = cnxpool.get_connection()
使用 DBUtils
, 、シンプルで信頼性の高い。
pip install DBUtils