锁概念
InnoDB存储引擎包含了三种行锁的算法,分别如下所示:
- Record Lock:行锁,针对的是单行记录;
- Gap Lock:间隙锁,锁定的是一个范围,但是不包含记录本身;
- Next-Key Lock:其实就是行锁+间隙锁,包含了记录本身和范围;
为什么需要间隙锁
数据库一般都有四种隔离级别,其中最常用的就是:已提交读(Read committed)和可重复读(Repeatable read);在已提交读隔离级别下会出现不可重复读的现象,而在可重复读隔离级别下会出现幻读(Phantom Read)的现象;
幻读:同一事务下,连续执行两次同样的SQL可能会导致不同的结果;
Innodb引擎在可重复读隔离级别下并不会出现幻读的现象,这主要是因为Innodb提供了多版本并发控制MVCC和间隙锁;常见的快照读
其实就是使用的MVCC,而当前读
就使用了间隙锁;
以下实例有两点说明:
- Innodb的可重复读隔离级别下,对当前读使用了间隙锁来解决幻读的问题,所以下面的实例都是基于默认隔离级别RR;
- Innodb的锁机制都依赖索引,所以下面的实例围绕索引来展开;
实战
无索引的情况
首先创建一个无索引的表,并初始化数据:
mysql> create table t1(a int);
mysql> insert into t1 values(1),(3),(5);
启动事务1,执行当前读:
mysql> begin;
mysql> select * from t1 where a=3 for update;
以上事务没有提交,再启动事务2,以下语句都被阻塞:
mysql> select * from t1 where a=3 for update;
mysql> insert into t1 values(1);
mysql> insert into t1 values(2);
mysql> insert into t1 values(5);
mysql> insert into t1 values(7);
但是这时候去执行快照读还是可以的:
mysql> select * from t1 where a=3;
可以发现在没有索引的情况下,除了快照读什么都干不了,感觉像是表被锁住了,表锁分为读和写锁,在写锁的情况下快照读同样被锁住,而在读锁的情况下可以使用快照读,类似上面无索引的情况;
mysql> lock table t1 read; ## 读锁
mysql> lock table t1 write; ## 写锁
mysql> unlock tables; ## 解锁
那是不是无索引的情况下就使用了表锁那,可以通过如下命令进行查看,首先看一下在表锁的情况下执行插入操作:
mysql> SHOW PROCESSLIST;
+-----+------+-----------------+------+---------+------+------------------------------+--------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+-----+------+-----------------+------+---------+------+------------------------------+--------------------------+
| 75 | ODBC | localhost:65316 | test | Query | 98 | Waiting for table level lock | insert into t1 values(7) |
+