A place in an InnoDB index data structure where new values could be inserted.
說白了gap就是索引樹中插入新記錄的空隙。相應的gap lock就是加在gap上的鎖,還有一個next-key鎖,是記錄+記錄前面的gap的組合的鎖。
To prevent phantoms, InnoDB uses an algorithm called next-key locking that combines index-row locking with gap locking.
InnoDB performs row-level locking in such a way that when it searches or scans a table index, it sets shared or exclusive
locks on the index records it encounters.Thus, the row-level locks are actually index-record locks. In addition, a next-key
lock on an index record also affects the 「gap」 before that index record. That is, a next-key lock is an index-record lock
plus a gap lock on the gap preceding the index record. If one session has a shared or exclusive lock on record R in an index,
another session cannot insert a new index record in the gap immediately before R in the index order.
簡單講就是防止幻讀。經過鎖阻止特定條件的新記錄的插入,由於插入時也要獲取gap鎖(Insert Intention Locks)。html
這和隔離級別有關,只在REPEATABLE READ或以上的隔離級別下的特定操做纔會取得gap lock或nextkey lock。mysql
http://dev.mysql.com/doc/refman/5.7/en/innodb-transaction-isolation-levels.htmlsql
2.1 REPEATABLE READ數據庫
... For consistent reads, there is an important difference from the READ COMMITTED isolation level: All consistent reads within the same transaction read the snapshot established by the first read. ... For locking reads (SELECT with FOR UPDATE or LOCK IN SHARE MODE), UPDATE, and DELETE statements, locking depends on whether the statement uses a unique index with a unique search condition, or a range-type search condition. For a unique index with a unique search condition, InnoDB locks only the index record found, not the gap before it. For other search conditions, InnoDB locks the index range scanned, using gap locks or next-key locks to block insertions by other sessions into the gaps covered by the range.
locking reads,UPDATE和DELETE時,除了對惟一索引的惟一搜索外都會獲取gap鎖或next-key鎖。即鎖住其掃描的範圍。session
下面對非惟一索引作一個測試:併發
建立表 tb2 ,注意,id 不是主鍵,是非惟一索引。測試
mysql> CREATE TABLE `tb2` ( -> `id` int(11) DEFAULT NULL, -> `c1` int(11) DEFAULT NULL, -> KEY `tb2_idx1` (`id`) -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
表中有3條記錄:spa
mysql> select * from tb2; +------+------+ | id | c1 | +------+------+ | 10 | 0 | | 20 | 0 | | 30 | 0 | +------+------+
開啓兩個事務 t1 和 t2,rest
在t1中,更新 id=20 的記錄code
mysql> update tb2 set c1=2 where id=20; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0
在t2中,執行插入操做,發現[10,30)範圍不能插入數據。
mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> insert into tb2 values(9,4); Query OK, 1 row affected (0.00 sec) mysql> insert into tb2 values(10,4); ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction mysql> insert into tb2 values(19,4); ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction mysql> insert into tb2 values(20,4); ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction mysql> insert into tb2 values(21,4); ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction mysql> insert into tb2 values(29,4); ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into tb2 values(30,4); Query OK, 1 row affected (0.01 sec)
再看看更新操做
mysql> update tb2 set c1=4 where id=10; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> update tb2 set c1=4 where id=20; ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction mysql> update tb2 set c1=4 where id=30; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0
能夠看到,update除了id=20的記錄之外,其餘的update操做都成功了。
結論:若是 t1 的表掃描沒有用到索引,那麼gap或next-key鎖住的範圍是該記錄以及該記錄先後的gap(包括該記錄前一條記錄,但不包括後一條記錄),這個範圍內不能夠插入值,但能夠更新值(除了被鎖住的那條記錄)。
For locking reads (SELECT with FOR UPDATE or LOCK IN SHARE MODE), UPDATE statements, and DELETE statements, InnoDB locks only index records, not the gaps before them, and thus permits the free insertion of new records next to locked records.
只會鎖住已有記錄,不會加gap鎖。
This level is like REPEATABLE READ, but InnoDB implicitly converts all plain SELECT statements to SELECT ... LOCK IN SHARE MODE if autocommit is disabled.
和REPEATABLE READ的主要區別在於把普通的SELECT變成SELECT ... LOCK IN SHARE MODE,即對普通的select都會獲取gap鎖或next-key鎖。
在「consistent-read」時,REPEATABLE READ下看到是事務開始時的快照,即便其它事務插入了新行一般也是看不到的,因此在常見的場合能夠避免幻讀。 可是,"locking read"或更新,刪除時是會看到已提交的修改的,包括新插入的行。
下面看一個例子
mysql> START TRANSACTION; Query OK, 0 rows affected (0.00 sec) mysql> select id,c1 from tb1 where id=1; +----+------+ | id | c1 | +----+------+ | 1 | 100 | +----+------+ row in set (0.00 sec)
SESSION 2:
mysql> update tb1 set c1=101 where id =1; Query OK, 1 row affected (0.03 sec) Rows matched: 1 Changed: 1 Warnings: 0
SESSION 1:
mysql> select id,c1 from tb1 where id=1 LOCK IN SHARE MODE; +----+------+ | id | c1 | +----+------+ | 1 | 101 | +----+------+ row in set (0.00 sec) mysql> select id,c1 from tb1 where id=1; +----+------+ | id | c1 | +----+------+ | 1 | 100 | +----+------+ row in set (0.00 sec) mysql> update tb1 set c1=c1+1000 where id=1; Query OK, 1 row affected (0.02 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select id,c1 from tb1 where id=1; +----+------+ | id | c1 | +----+------+ | 1 | 1101 | +----+------+ row in set (0.00 sec)
上面update的行爲違反了REPEATABLE READ的承諾,看到了事務開始後其它事務的併發更新。這對應用開發須要特別注意,這種狀況下其它數據庫一般都是報錯的。
RR和RC相比還有一個重要的區別,RC下,掃描過但不匹配的記錄不會加鎖,或者是先加鎖再釋放,即semi-consistent read。但RR下掃描過記錄都要加鎖。這個差異對有全表掃描的更新的場景影響極大。詳細參考http://hedengcheng.com/?p=771,關於MySQL的加鎖處理,這篇文章講得很透徹!