5

これには簡単な解決策があると確信していますが、今のところ見つけることができていません。分離レベルがSERIALIZABLEに設定されたInnoDBMySQLデータベースを提供し、次の操作を実行しました。

BEGIN WORK;
SELECT * FROM users WHERE userID=1;
UPDATE users SET credits=100 WHERE userID=1;
COMMIT;

トランザクション内のselectが発行されるとすぐに、userID = 1に対応する行が、トランザクションが完了するまで読み取り用にロックされていることを確認したいと思います。現在のところ、この行へのUPDATEは、トランザクションが進行中の場合はトランザクションが終了するのを待ちますが、SELECTは単に前の値を読み取ります。これがこの場合の予想される動作であることは理解していますが、トランザクションが終了して値を返すまでSELECTが待機するように行をロックする方法があるのでしょうか。

私がそれを探している理由は、ある時点で、十分な同時ユーザーがいるときに、前のトランザクションが進行中に、他の誰かが「クレジット」を読み取って他の何かを計算する可能性があるためです。理想的には、他の誰かが実行するコードは、トランザクションが終了して新しい値を使用するのを待つ必要があります。そうしないと、不可逆的な非同期の問題が発生する可能性があります。

テーブル全体を読み取り用にロックしたくはなく、特定の行だけをロックしたいことに注意してください。

また、ブール値の「ロックされた」フィールドをテーブルに追加して、トランザクションを開始するたびに1に設定することもできますが、他に方法が絶対にない限り、これがここで最もエレガントなソリューションであるとは思えません。 mysqlを介してこれを直接処理します。

4

2 に答える 2

3

具体的には、回避策を見つけました。

SELECT ... LOCK IN SHARE MODEは、読み取られた行に共有モードロックを設定します。共有モードロックにより、他のセッションは行を読み取ることができますが、変更することはできません。読み取られた行は利用可能な最新のものであるため、まだコミットされていない別のトランザクションに属している場合、読み取りはそのトランザクションが終了するまでブロックされます。

出典

トランザクションデータに依存する重要なSELECTステートメントにLOCKINSHARE MODEを含めることができ、実際に現在のトランザクションが終了するのを待ってから行を取得できるようです。これを機能させるには、トランザクションでFOR UPDATEを明示的に使用する必要があります(私が示した元の例とは対照的です)。たとえば、次のようになります。

BEGIN WORK;
SELECT * FROM users WHERE userID=1 FOR UPDATE;
UPDATE users SET credits=100 WHERE userID=1;
COMMIT;

私が使用できるコードの他の場所:

SELECT * FROM users WHERE userID=1 LOCK IN SHARE MODE;

このステートメントはトランザクションにラップされていないため、ロックはすぐに解放され、後続のクエリには影響しませんが、トランザクション内でuserID = 1を含む行が更新用に選択されている場合、このステートメントはトランザクションが完了するまで待機します。それはまさに私が探していたものです。

于 2012-08-31T03:35:45.473 に答える
2

SELECT ...FORUPDATEロック読み取りを試すことができます。

SELECT ... FOR UPDATEは、利用可能な最新のデータを読み取り、読み取る各行に排他ロックを設定します。したがって、検索されたSQLUPDATEが行に設定するのと同じロックを設定します。

次のサイトにアクセスしてください:http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html

于 2012-04-27T17:14:28.883 に答える