web-dev-qa-db-ja.com

トランザクションの基本。 2つのトランザクションが同時に実行された結果はどうなりますか?

取引に関してかなり基本的な質問があります。
列量を持つテーブルAccountを想定します。これで、次のコードができました。

BEGIN  
$amount = SELECT amount FROM ACCOUNT WHERE id = 123;  
$amount = $amount - 100;  
UPDATE ACCOUNT SET amount = $amount WHERE id = 123;    
COMMIT

このスニペットが2つのトランザクション(T1とT2)で同時に実行されるとどうなりますか?
MySQLではデフォルトで分離レベルがREPEATABLE READ
最初の金額が500だとしましょう。
では、2つのトランザクションが完了すると、最終的な金額は300または100になりますか?
つまり、両方がテーブルから読み取りを開始すると、共有ロックであると推測します。
テーブルを更新すると、排他ロックが取得されますか?では、他のトランザクションはどうなりますか?開始時に「表示」した量、つまり500で続行しますか?

4
Jim

REPEATABLE READ 分離レベルには暗黙の共有ロックはありません。

トランザクションシーケンスを次のように変更する必要があります。

分離レベル

[〜#〜] serializable [〜#〜] 分離レベルに切り替えて、自動コミットを無効にすることができます。これにより、行は共有モードで暗黙的にロックされます。

REPEATABLE READ または READ COMMITTED を使用したままにする場合は、UPDATEを実行する前に、手動で SELECT ... FOR UPDATE を呼び出す必要があります。

QUERY(オプションの提案)

また、実験する必要があります

UPDATE ACCOUNT SET amount = amount - 100 WHERE id = 123; 

無効化された自動コミットおよび [〜#〜] serializable [〜#〜] 分離レベルとともに。

量を変更するために2つのクエリ(SELECTとUPDATE)を実行しているので、これを提案しました。 1つのクエリで実行できます。

あなたの質問

REPEATABLE READ で暗黙的ではなく読み取りをロックするため、500が2回使用される可能性があります。

あなたが尋ねた

分離レベルを接続レベルに設定することは可能ですか?グローバル分離レベルに触れていませんか?

はい、 セッション内で分離レベルを設定できます

SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL   {
    REPEATABLE READ
  | READ COMMITTED
  | READ UNCOMMITTED
  | SERIALIZABLE    }
2
RolandoMySQLDBA

2つのトランザクションがコミットされた後の量は400になると思います。

テストしてみましょう。セッション#1:

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT amount FROM ACCOUNT WHERE id = 123;
+--------+
| amount |
+--------+
|    500 |
+--------+
1 row in set (0.00 sec)

セッション#2:

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT amount FROM ACCOUNT WHERE id = 123;
+--------+
| amount |
+--------+
|    500 |
+--------+
1 row in set (0.00 sec)

セッション#1

mysql> UPDATE ACCOUNT SET amount = 400 WHERE id = 123;
Query OK, 0 rows affected (2.63 sec)
Rows matched: 1  Changed: 0  Warnings: 0

セッション#2

mysql> UPDATE ACCOUNT SET amount = 400 WHERE id = 123;
... waiting for a RW lock held by session #1

セッション#1

mysql> commit;

セッション#2

Query OK, 0 rows affected (2.63 sec)
Rows matched: 1  Changed: 0  Warnings: 0

mysql> commit;
Query OK, 0 rows affected (0.01 sec)

いずれかのセッション:

mysql> SELECT amount FROM ACCOUNT WHERE id = 123;
+--------+
| amount |
+--------+
|    400 |
+--------+
1 row in set (0.00 sec)
2
akuzminsky