この章は何
- 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でロックを解放した方がよい