SQLAlchemyが選択したデータを返さない
-
10-07-2019 - |
質問
SQLAlchemyをORMとして使用しているのは、しばらくの間構築してきたアプリケーションです。
これまでのところ、実装して使用するのは非常に簡単なORMでしたが、最近取り組んでいる機能には永続的な&が必要です。 MySQLおよびPythonで構築した分散キュー(リスト&ワーカー)スタイルの実装。
スケーリングされた環境でテストするまで、すべてうまくいきました。
InnoDBの行レベルのロックを使用して、各行が一度だけ読み取られるようにし、行がロックされている間、「 in_use
」値を更新して、他のエントリをつかまないでください。
MySQLは" NOWAIT"を提供しないため、 PostgreやOracleのようなメソッドは、ワーカースレッドがハングし、ロックされた行が利用可能になるのを待つロックの問題に遭遇しました。
この制限を克服するために、必要なすべての処理を1つのステートメントにまとめて、ORMの execute()
メソッドで実行しようとしました、ただし、SQLAlchemyはクエリ結果を返すことを拒否しています。
例を示します。
私のSQLステートメントは次のとおりです。
SELECT id INTO @update_id FROM myTable WHERE in_use=0 ORDER BY id LIMIT 1 FOR UPDATE;
UPDATE myTable SET in_use=1 WHERE id=@update_id;
SELECT * FROM myTable WHERE id=@update_id;
そして、このコードをコンソールで実行します:
engine = create_engine('mysql://<user details>@<server details>/myDatabase', pool_recycle=90, echo=True)
result = engine.execute(sqlStatement)
result.fetchall()
この結果を取得する場合のみ
[]
データベースで更新が有効になるのを確認できるため、ステートメントが実行されていることは確かです。mysqlターミナルまたは他のツールを使用して実行すると、変更された行が返されます。 返された行を確認したくないSQLAlchemyのようです。
ORMが応答を確実に取得するために行う必要のある具体的なことはありますか?
乾杯
解決
3つのクエリを実行し、MySQLdbはそれぞれの結果セットを作成します。最初の結果を取得してから、 cursor.nextset()
を呼び出し、2番目の結果を取得する必要があります。
これはあなたの質問に答えますが、ロックの問題を解決しないため、役に立ちません。 FOR UPDATEが最初にどのように機能するかを理解する必要があります。返される行をトランザクションの終了までロックします。長いロック待機を回避するには、できるだけ短くする必要があります: SELECT ... FOR UPDATE
、 UPDATE SET in_use = 1 ...
、 COMMIT
。実際には、それらを単一のSQLステートメントに入れる必要はありません。3つの execute()
呼び出しでも問題ありません。ただし、長い計算の前にコミットする必要があります。そうしないと、ロックが長すぎるため、 in_use
(オフラインロック)を更新しても意味がありません。そして、ORMを使用しても同じことができることを確認してください。