非ユニークインデックスの場合は、等価条件であっても範囲条件に近いロックを取るという話。空振ってなくても思ったよりロック広い??ってなるケースとかはこれの可能性がありそう。ユニーク制約の付いていないカラムには同値を入れることができるため、現在存在しているnot_uniquのレコードだけロックしてもレコードの挿入を防ぐことはできない。
mysql> desc t1; +-------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+-------+ | ID | int(11) | NO | PRI | NULL | | | name | varchar(30) | YES | MUL | NULL | | +-------+-------------+------+-----+---------+-------+ mysql> show index from t1\G *************************** 1. row *************************** Table: t1 Non_unique: 0 Key_name: PRIMARY Seq_in_index: 1 Column_name: ID Collation: A Cardinality: 3 Sub_part: NULL Packed: NULL Null: Index_type: BTREE Comment: Index_comment: *************************** 2. row *************************** Table: t1 Non_unique: 1 Key_name: id_index Seq_in_index: 1 Column_name: name Collation: A Cardinality: 3 Sub_part: NULL Packed: NULL Null: YES Index_type: BTREE Comment: Index_comment: mysql> select * from t1; +----+------+ | ID | name | +----+------+ | 3 | aaa | | 6 | bbb | | 9 | ddd | | 21 | ggg | +----+------+
例えばこんなテーブルがあるとしてnameは非ユニーク。ここでdddで条件検索で排他ロックをとってみる
mysql> begin; mysql> select * from t1 where name = 'ddd' for update;
この時のinnodbの値はこんな感じ。PRIMARYはレコードロックをとっているがセカンダリインデックスの方はlock_mode X locks gap before rec
となっておりGAPロック持ちとなっている
---TRANSACTION 1986, ACTIVE 17 sec 4 lock struct(s), heap size 1136, 3 row lock(s) MySQL thread id 2, OS thread handle 139829509609216, query id 396 localhost root starting show engine innodb status TABLE LOCK table `test`.`t1` trx id 1986 lock mode IX RECORD LOCKS space id 24 page no 4 n bits 72 index id_index of table `test`.`t1` trx id 1986 lock_mode X Record lock, heap no 4 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 0: len 3; hex 646464; asc ddd;; 1: len 4; hex 80000009; asc ;; RECORD LOCKS space id 24 page no 3 n bits 80 index PRIMARY of table `test`.`t1` trx id 1986 lock_mode X locks rec but not gap Record lock, heap no 4 PHYSICAL RECORD: n_fields 4; compact format; info bits 0 0: len 4; hex 80000009; asc ;; 1: len 6; hex 000000000707; asc ;; 2: len 7; hex a70000011b0128; asc (;; 3: len 3; hex 646464; asc ddd;; RECORD LOCKS space id 24 page no 4 n bits 72 index id_index of table `test`.`t1` trx id 1986 lock_mode X locks gap before rec Record lock, heap no 5 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 0: len 3; hex 676767; asc ggg;; 1: len 4; hex 80000015; asc ;;
別のトランザクションを起動してinsertを試してみる。bbb ~ gggまでのGAPに対してロックが取られているのがわかる
mysql> insert into t1 values(1, 'fff'); ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction mysql> insert into t1 values(8, 'aaab'); Query OK, 1 row affected (0.01 sec)
supremumとのロック
存在するレコードの末尾を排他ロックするとsupremumとのGAPロックを獲得するので予想外の範囲のロックを取る。
mysql> select * from t1 where name = 'ggg' for update; +----+------+ | ID | name | +----+------+ | 21 | ggg | +----+------+ ------------ TRANSACTIONS ------------ Trx id counter 2016 Purge done for trx's n:o < 2015 undo n:o < 0 state: running but idle History list length 0 LIST OF TRANSACTIONS FOR EACH SESSION: ---TRANSACTION 421304856972944, not started 0 lock struct(s), heap size 1136, 0 row lock(s) ---TRANSACTION 421304856972024, not started 0 lock struct(s), heap size 1136, 0 row lock(s) ---TRANSACTION 2015, ACTIVE 183 sec 3 lock struct(s), heap size 1136, 3 row lock(s) MySQL thread id 2, OS thread handle 139829509609216, query id 445 localhost root starting show engine innodb status TABLE LOCK table `test`.`t1` trx id 2015 lock mode IX RECORD LOCKS space id 24 page no 4 n bits 80 index id_index of table `test`.`t1` trx id 2015 lock_mode X Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0 0: len 8; hex 73757072656d756d; asc supremum;; Record lock, heap no 5 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 0: len 3; hex 676767; asc ggg;; 1: len 4; hex 80000015; asc ;; RECORD LOCKS space id 24 page no 3 n bits 80 index PRIMARY of table `test`.`t1` trx id 2015 lock_mode X locks rec but not gap Record lock, heap no 5 PHYSICAL RECORD: n_fields 4; compact format; info bits 0 0: len 4; hex 80000015; asc ;; 1: len 6; hex 000000000707; asc ;; 2: len 7; hex a70000011b0134; asc 4;; 3: len 3; hex 676767; asc ggg;;
こうなってしまうとggg以降へのinsertができなくなってしまう。
mysql> insert into t1 values(11, 'gggg'); ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction mysql> insert into t1 values(8, 'hhhh'); ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction