取引に関してかなり基本的な質問があります。
列量を持つテーブル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で続行しますか?
REPEATABLE READ 分離レベルには暗黙の共有ロックはありません。
トランザクションシーケンスを次のように変更する必要があります。
[〜#〜] serializable [〜#〜] 分離レベルに切り替えて、自動コミットを無効にすることができます。これにより、行は共有モードで暗黙的にロックされます。
REPEATABLE READ または READ COMMITTED を使用したままにする場合は、UPDATEを実行する前に、手動で SELECT ... FOR UPDATE を呼び出す必要があります。
また、実験する必要があります
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つのトランザクションがコミットされた後の量は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)