MySQLのロック
MySQLのロックで事故ったことがないので体感でよく分からん部分が多いので調べてみる。
デッドロック
複数のトランザクションが相互にロックを保持しようとする時に循環参照が発生し処理が進まなくなる問題
MySQLのinnodbではデッドロックが発生すると自動で検知して片方のトランザクションをロールバックする
トランザクション
アトミックに処理される複数のSQLの集まり。成功するか失敗するかの二択。データの一貫性を維持するために使われたりする
MySQLではautocommitが有効になっているので全てのクエリは独立したトランザクションでラップされて自動でコミットされる
isolationレベル
トランザクション同士のisolationの仕方に種類がある
MySQLではREAD UNCOMMITTED, READ COMMITTED,REPEATABLE READ, SERIALIZABLEがある
REPEATABLE-READでは
あるトランザクション内から発行されるSQLの結果は全て同じsnapshotから取得される、という挙動になる
exclusiveロックを持つ行への書き込みはできないが読み取りはできる
ロックの種類
ロック
shared (S)(共有)
READで複数のtransactionが保持するロック。
exclusive (X)(排他)
WRITEで一つのtransactionだけが保持できるロック。
テーブルロック
テーブル全体がロックされる。
InnoDBだとあまり発生しない。意図的にread/writeのどちらでも発生させることは可能。
行ロック
いくつかの行のみがロックされる。
IS LOCK / IX LOCK
intention lock = トランザクションが意図的にロックする
テーブルレベルと行レベルのロックの解決に使われる
行ロックを取得する前にIS LOCK / IX LOCKがまず行われる
Gap locks ≒ 行と行と間の隙間の行もロックする
index間のロックを行う
index値を持つ行と行の間にあるギャップ
先頭のindex値を持つ行の前のギャップ
末尾のindex値を持つ行の後のギャップ
データがない部分に対しても排他ロックがかかり、そこにデータを入れようとしてもロック待ちとなる。
idが1~3の行にfor updateでトランザクションを発行 => 別のトランザクションでid=2に行をinsertしたい => 排他ロックがかけられてるので失敗
code:イメージ
id name status
1 hoge 排他ロック
排他ロック <- id=2の行は無いがギャップロックがかかっている
3 bar 排他ロック
Next-Key locks
ギャップロックと行ロックの組み合わせで、インデックスレコードに対するレコードロックと、そのインデックスレコードの前にあるギャップに対するギャップロックとを組み合わせたもの
idが4未満の行にfor updateでトランザクションを発行 => 別のトランザクションでid=4に行をinsertしたい => 排他ロックがかけられてるので失敗
code:イメージ
select * from table where id < 4 for update
id name status
1 hoge 排他ロック
2 poo 排他ロック
3 bar 排他ロック
4 foo 排他ロック <- id < 4だから1~3までを行ロックする。そしてネクストキーロックでid4もロックされる
5 goo null <- id=5はロックがかかってない
ざっくりまとめ
テーブルロックと行(レコード)に対するロックがあります。
ロックの強度は排他と共有の2種類があります(MySQL5.7からはその中間である共有排他(SX)ロックがありますがいったん置いときます)
行(row)に対するロックは次の3種類です。
行(レコード)ロック
ギャップロック
ネクストキーロック