Next-Key Locks 一個next-key lock 是 一個record lock 在index record 和 一個區間鎖 在一個區間在index record以前 InnoDB 執行 row-level locking 以這樣一種方式當它搜索或者掃描 一個表的索引, 它設置共享或者排它鎖在index records. 所以, row-level locks 其實是 index-record locks. 一個next-key lock 在一個index record 也影響區間在那個index record 以前。 也就是說,一個next-key lock 是一個Index-record 加上一個區間鎖在index record 以前的區間。 若是一個會話有一個共享或者排它鎖在記錄R上在一個索引上, 另外的會話不能插入一個新的index record 在這個區間 假設一個Index 包含值10,11,13,20. 可能的next-key locks 對於這個index包含了下面的時間間隔, 一個圓括號表示排除間隔端點 一個方括號表示包含間隔端點 (negative infinity, 10] (10, 11] (11, 13] (13, 20] (20, positive infinity) 在最後的區間, next-key lock locks 的區間在最大值的上界在index和上界的僞記錄 有一個值高於任何值在index裏。 上界限不是一個真正的index record.因此,實際上, next-key lock locks只有區間在最大索引值後面的區間 默認狀況下, InnoDB 工做在REPEATABLE READ 事務隔離級別下 innodb_locks_unsafe_for_binlog 系統變量被禁用, 在那種狀況下,能夠使用next-key locks來搜索和索引掃描,來防止幻讀行 Session 1: mysql> explain select * from t1 where id BETWEEN 5 and 7 for update; +----+-------------+-------+-------+---------------+---------+---------+------+------+-----------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+------+------+-----------------------+ | 1 | SIMPLE | t1 | range | t1_idx1 | t1_idx1 | 5 | NULL | 3 | Using index condition | +----+-------------+-------+-------+---------------+---------+---------+------+------+-----------------------+ 1 row in set (0.00 sec) mysql> select * from t1 where id BETWEEN 5 and 7 for update; +-----+------+------+ | sn | id | info | +-----+------+------+ | 239 | 5 | a5 | | 240 | 6 | a6 | | 241 | 7 | a7 | +-----+------+------+ 3 rows in set (0.00 sec) Session 2: mysql> update t1 set id=300 where id=3; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> rollback; Query OK, 0 rows affected (0.01 sec) mysql> update t1 set id=500 where id=5; ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction mysql> update t1 set id=600 where id=6; ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction mysql> update t1 set id=600 where id=4; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> rollback; Query OK, 0 rows affected (0.00 sec) mysql> update t1 set id=600 where id=7; ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction mysql> update t1 set id=800 where id=8; ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction mysql> update t1 set id=800 where id=9; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> rollback; Query OK, 0 rows affected (0.01 sec) 會鎖住 5,6,7,8 4條記錄 繼續測試: Session 1: mysql> show index from t1; +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | t1 | 0 | PRIMARY | 1 | sn | A | 11 | NULL | NULL | | BTREE | | | | t1 | 0 | t1_idx1 | 1 | id | A | 11 | NULL | NULL | YES | BTREE | | | +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 2 rows in set (0.00 sec) mysql> select * from t1 where id=7 for update; +-----+------+------+ | sn | id | info | +-----+------+------+ | 241 | 7 | a7 | +-----+------+------+ 1 row in set (0.00 sec) Session 2: mysql> update t1 set id=800 where id=8; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> rollback; Query OK, 0 rows affected (0.01 sec) 去掉Id列的索引繼續測試: Session 1: mysql> show index from t1; +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | t1 | 0 | PRIMARY | 1 | sn | A | 11 | NULL | NULL | | BTREE | | | | t1 | 0 | t1_idx1 | 1 | id | A | 11 | NULL | NULL | YES | BTREE | | | +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 2 rows in set (0.00 sec) mysql> alter table t1 drop index t1_idx1; Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> show index from t1; +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | t1 | 0 | PRIMARY | 1 | sn | A | 11 | NULL | NULL | | BTREE | | | +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 1 row in set (0.00 sec) mysql> select * from t1 where id=7 for update; +-----+------+------+ | sn | id | info | +-----+------+------+ | 241 | 7 | a7 | +-----+------+------+ 1 row in set (0.00 sec) Session 2: Database changed mysql> update t1 set id=800 where id=8; --HANG /************************************************ mysql> explain select * from t1 where id=7 for update; +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | t1 | ALL | NULL | NULL | NULL | NULL | 11 | Using where | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec) mysql> show index from t1; +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | t1 | 0 | PRIMARY | 1 | sn | A | 11 | NULL | NULL | | BTREE | | | | t1 | 1 | t1_idx1 | 1 | id | A | 11 | NULL | NULL | YES | BTREE | | | +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 2 rows in set (0.00 sec) mysql> explain select * from t1 where id=7 for update; +----+-------------+-------+------+---------------+---------+---------+-------+------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+---------+---------+-------+------+-------+ | 1 | SIMPLE | t1 | ref | t1_idx1 | t1_idx1 | 5 | const | 1 | NULL | +----+-------------+-------+------+---------------+---------+---------+-------+------+-------+ 1 row in set (0.00 sec) 下面來舉個手冊上的例子看什麼是next-key lock。假如一個索引的行有10,11,13,20 那麼可能的next-key lock的包括: (無窮小, 10] (10,11] (11,13] (13,20] (20, 無窮大) (這裏無窮大爲何不是閉合?你數學不到家~~) 舉例測試: mysql> desc t100; +-------+---------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+---------+------+-----+---------+----------------+ | sn | int(11) | NO | PRI | NULL | auto_increment | | id | int(11) | YES | | NULL | | +-------+---------+------+-----+---------+----------------+ 2 rows in set (0.00 sec) mysql> show create table t100\G; *************************** 1. row *************************** Table: t100 Create Table: CREATE TABLE `t100` ( `sn` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) DEFAULT NULL, PRIMARY KEY (`sn`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 1 row in set (0.00 sec) ERROR: No query specified Session 1: mysql> mysql> show index from t100; +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | t100 | 0 | PRIMARY | 1 | sn | A | 11 | NULL | NULL | | BTREE | | | +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 1 row in set (0.00 sec) mysql> select * from t100; +----+------+ | sn | id | +----+------+ | 1 | 7 | | 2 | 9 | | 3 | 10 | | 4 | 12 | | 5 | 13 | | 6 | 14 | | 7 | 15 | | 8 | 22 | | 9 | 23 | | 10 | 24 | | 11 | 25 | +----+------+ 11 rows in set (0.00 sec) mysql> update t100 set id=100 where id=1; Query OK, 0 rows affected (0.00 sec) Rows matched: 0 Changed: 0 Warnings: 0 id列上沒有索引,致使: Session 2: mysql> insert into t100(id) values(100); t100表全部記錄鎖住 /*************** mysql> show index from t100; +-------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +-------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | t100 | 0 | PRIMARY | 1 | sn | A | 11 | NULL | NULL | | BTREE | | | | t100 | 1 | t1oo_idx1 | 1 | id | A | 11 | NULL | NULL | YES | BTREE | | | +-------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ mysql> select * from t100; +----+------+ | sn | id | +----+------+ | 1 | 7 | | 2 | 9 | | 3 | 10 | | 4 | 12 | | 5 | 13 | | 6 | 14 | | 7 | 15 | | 8 | 22 | | 9 | 23 | | 10 | 24 | | 11 | 25 | +----+------+ 11 rows in set (0.00 sec) mysql> delete from t100 where id=21; Query OK, 0 rows affected (0.00 sec) Session 2: mysql> insert into t100(id) values (15); ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction mysql> insert into t100(id) values (16); ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction mysql> insert into t100(id) values (17); ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction mysql> mysql> insert into t100(id) values (18); ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction mysql> insert into t100(id) values (19); ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction mysql> insert into t100(id) values (20); ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction mysql> insert into t100(id) values (21); ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction 鎖15-21 之間 行鎖加鎖對象永遠是索引記錄,由於innodb中表即索引 在(三)種,a=21也是不存在,可是在表裏面21先後都有記錄,所以這裏next-key lock的區間也就是(15,21],所以不在這個區間內的均可以插入。 記錄鎖---鎖單條記錄;區間鎖---鎖一個開區間;next-key 鎖---前面二者的結合