3,Next-Key Lock:1+2,锁定一个范围,并且锁定记录本身。对于行的查询,都是采用该方法,主要目的是解决幻读的问题。
如果采用MySQL默认的事务隔离级别(可以重复读:repeatable read)
可重复读取(Repeatable Read):防止(避免)不可重复读取和脏读,但是有时可能出现幻读数据。这可以通过“共享读锁”和“排他写锁”实现。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。
- 脏读:一个事务还未提交,另外一个事务访问此事务修改的数据,并使用,读取了事务中间状态数据。
- 幻读:一个事务读取2次,得到的记录条数不一致,由于2次读取之间另外一个事务对数据进行了增删。
- 不可重复读:一个事务读取同一条记录2次,得到的结果不一致,由于在2次读取之间另外一个事务对此行数据进行了修改。
一、正常查询
在4个事务隔离级别中,除了在串行化(Serializable)时会加共享锁,其他的都不加锁,即快照读。
二、加了锁的查询
加锁select主要是指:
-
select ... for update
-
select ... in share mode
1.当使用唯一索引来搜索唯一行的语句时,使用记录锁(record lock)。如:
select * from t where id = 10 for update; # id是唯一索引列
2.其他情况,包括id列没有索引或者是非唯一索引又或者是搜索条件里有多个查询条件(使每个列都有唯一索引),则使用间隙锁与临键锁。
临键锁是间隙锁和记录锁的组合。
三、update和delete
1. 当使用唯一索引列确定的唯一数据行上进行的update/delete,也使用记录锁:
update t set number=10 where id=1;
2. 其他情况,包括id列没有索引或者是非唯一索引又或者是搜索条件里有多个查询条件(使每个列都有唯一索引),则加排他临键锁(exclusive next-key lock)。
注意:如果update的是聚集索引记录,则对应的二级索引记录也会被隐式加锁,这是由InnoDB索引的实现机制决定的:二级过引的叶子上存的是聚集索引的主键值,当检索二级索引时,会二次扫描聚集索引。
四、insert
insert会用排它锁封锁被插入的索引记录,然后在插入区间加插入意向锁(insert intention lock)。
总结:
读已提交和可重复读级别下加锁情况
正常查询(不加update)的话,都是快照读!
如果select的时候加了for update 就会用行锁
唯一索引 唯一行 就是行锁
没有索引 或者普通索引,或者有多个条件,那么就是用间隙锁
update和delete的时候就是加行锁 (唯一索引)
没有索引 或者普通索引,或者有多个条件,那么就是用间隙锁
insert会用排它锁封锁被插入的索引记录,然后在插入区间加插入意向锁(insert intention lock)。 也就是只锁访问的资源,不会锁全表
只要不是插入相同的index record,多个事务向同一个gap插入记录是不会冲突的。虽然插入意向锁之间不会互相阻塞,但是插入意向锁与涉及本区间的行锁们可是不兼容的,会互相阻塞,这也是造成死锁的一大主因。
读已提交
查询用主键和用唯一索引都是只锁单独一条记录,以及对应的聚集索引上的项加锁 也就是行锁
普通索引的话 会对满足条件的对应记录加上锁
如果没有走索引,也只会对满足条件的对应记录上锁
可重复读
查询用主键和用唯一索引都是只锁单独一条记录,以及对应的聚集索引上的项加锁 也就是行锁
普通索引的话 会对旁边的也加锁,是gap锁
如果没有走索引,会对表中所有记录以及间隙加锁