この章は何
- MySQLの
SELECT FOR UPDATE
の挙動について理解するための章
SELECT FOR UPDATEとは
- レコードに対して排他ロックをかけるための処理
- 他のトランザクションから
SELECT FOR UPDATE
やUPDATE
、DELETE
をできなくする - ただし単なる
SELECT
は可能
- 他のトランザクションから
SELECT FOR UPDATEを使う上での注意点
- トランザクションの中で実行する必要がある(トランザクション内じゃないとただの
SELECT
になってしまう) - 処理が終わったら早めに
COMMIT
やROLLBACK
を行う(行ロックを長時間専有すると他の処理ができないため)
実際に手元で挙動を確認してみる
事前に用意するテーブル
- bank_accounts(銀行口座)
- balance(残高), integer
試すこと
- 他のトランザクションから
SELECT FOR UPDATE
できないこと - 他のトランザクションから
UPDATE
できないこと - 他のトランザクションから単なる
SELECT
はできること - トランザクション内で
SELECT FOR UPDATE
しないとただのSELECT
になること
他のトランザクションからSELECT FOR UPDATEできないこと
左右両方のターミナルで
BEGIN
してトランザクションを開始する左ターミナルで
SELECT FOR UPDATE
を実行する(行ロックをかける)右ターミナルで
SELECT FOR UPDATE
を実行すると処理が待たされる(行ロックがかかっているため参照できない)
他のトランザクションでSELECT FOR UPDATEしようとすると処理が待たされる左ターミナルで
COMMIT
すると右ターミナルでSELECT FOR UPDATE
の処理が動く(左ターミナルが行ロックを解放したため)
行ロックが解放されると待たされていた処理が実行される
他のトランザクションからUPDATEできないこと
左右両方のターミナルで
BEGIN
してトランザクションを開始する左ターミナルで
SELECT FOR UPDATE
を実行する(行ロックをかける)右ターミナルで
UPDATE
を実行すると処理が待たされる(行ロックがかかっているため)
他のトランザクションでUPDATEしようとすると処理が待たされる左ターミナルで
COMMIT
すると右ターミナルでUPDATE
の処理が動く(左ターミナルが行ロックを解放したため)
行ロックが解放されると待たされていた処理が実行される
他のトランザクションから単なるSELECTはできること
- 左右両方のターミナルで
BEGIN
してトランザクションを開始する - 左ターミナルで
SELECT FOR UPDATE
を実行する(行ロックをかける) - 右ターミナルで
SELECT
を実行すると行を取得できる
行ロックがかかっていても単なるSELECTなら他のトランザクションからも実行できる
トランザクション内でSELECT FOR UPDATEしないとただのSELECTになること
左ターミナルで
BEGIN
せずにSELECT FOR UPDATE
を実行する
BEGINせずにSELECT FOR UPDATEを実行する続いて右ターミナルで
SELECT FOR UPDATE
を実行すると取得できてしまう(つまり左ターミナルで実行したSELECT FOR UPDATE
で行ロックがかかっていない)
他のトランザクションからSELECT FOR UPDATEを実行できてしまう
例に関する補足
上記の例はトランザクション分離レベルがREPEATABLE READ
の場合の挙動なので、別のトランザクション分離レベルが設定されている場合は挙動が少し異なります。
とはいえMySQLのトランザクション分離レベルはデフォルトでREPEATABLE READ
になっており変更することもあまりないので、トランザクション分離レベルによる挙動の違いはそこまで意識する必要はないと思います。
まとめ
SELECT FOR UPDATE
はレコードに対して排他ロックをかけるための処理- 他のトランザクションから
SELECT FOR UPDATE
やUPDATE
、DELETE
ができなくなる - ただし単なる
SELECT
は可能
- 他のトランザクションから
SELECT FOR UPDATE
はトランザクションの中で実行する必要がある- 処理が終わったら早めに
COMMIT
やROLLBACK
でロックを解放した方がよい