锁
锁机制用于管理对共享资源的并发访问。
lock和latch
在数据库中,lock和Latch都称为锁,但是两者意义不同。
latch称为闩锁(shuang suo),其要求锁定的时间必须非常短。若持续的时间长,则应用的性能会非常差。在InnoDB存储引擎中,latch又分为mutex互斥锁 和 rwLock读写锁。其目的是为了保证并发线程操作临界资源的正确性。通常没有死锁的检测机制。
lock的对象是事务,用来锁定的是数据库中的对象,如表、页、行。并且一般lock的对象仅在事务commit或者rollback后进行释放。有死锁检测机制。
通过show engine innodb mutex可以查看InnoDB存储引擎的中latch,具体字段详情如下表:
锁的类型
有几个索引,需要分别向索引加锁。
共享锁、排他锁
InnoDB存储引擎实现了如下两种标准的行级锁:
共享锁(S Lock):允许事务读一行数据
排他锁(X Lock):允许事务删除 或 更新一行数据
如果一个事务T1已经获取了行r的共享锁,那么另外的事务T2可以立即获得行r的共享锁。因为读取并不会改变行的数据,所以可以多个事务同时获取共享锁,称这种情况为锁兼容。但若有其他的事务T3想获得行R的排他锁,则其必须等待事务T1、T2释放行r上面的共享锁,称这种情况为锁不兼容。下面显示了共享锁和排他锁的兼容性:
从表6-3可以看出X锁与任何锁都不兼容,而S锁仅和S锁兼容。S锁和X锁都是行锁,兼容是指对同一行记录锁的兼容情况。
普通 select 语句默认不加锁,而CUD操作默认加排他锁。
记录锁
Record Lock,仅锁定一行记录(如共享锁、排他锁)
记录锁总是会去锁定索引记录,如果表在建立的时候,没有设置任何一个索引,那么InnoDB会使用隐式的主键来进行锁定。
查询条件的列是唯一索引的情况下,临建锁退化为记录锁
间隙锁
Gap Lock,锁定一个范围,但不包含记录本身。
关闭间隙锁的2种方式:
(1)将事务隔离级别变为read committed
(2)将参数innodb_locks_unsafe_for_binlog设置为1
在上述配置下,除了外键和唯一性检查依然需要间隙锁,其余情况仅适用行锁进行锁定。
临键锁
Next-Key Lock,等于记录锁 + 临键锁,锁定一个范围,并且锁定记录本身。主要是阻止多个事务将记录插入到同一个范围内,从而避免幻读。
假如一个索引有10、11、13、20这四个值,那么该索引可能被锁定的区间为:
若事务T1已经通过临键锁锁定了如下范围:
当插入新的记录12时,则锁定的范围变成:
当查询的索引是唯一索引的时候,InnoDB会将临键锁优化成记录锁,从而提高并发。这时候,将不再由间隙锁避免幻读的问题,但是试验了下,即使优化成记录锁,也不会有幻读的问题,其实是因为MVCC,在可重复读的情况下,SELECT操作只会查找行版本号小于当前事务版本号的记录,其他事务(事务开启时间比当前事务晚)新插入的记录版本号不满足条件,就不会查出来。
对于辅助索引,当执行类似select * from z where b = 3 for update;加锁语句时,会加上临键锁,并且下一个键值的范围也会加上间隙锁。
值得注意的是,对于唯一键值的锁定,由临键锁优化为记录锁,仅存在于查询所有的唯一索引。若唯一索引由多列组成,而查询仅是查找多个唯一索引中的一个,那么查询其实是range类型查询,而不是point类型查询,故InnoDB存储引擎还是继续使用临键锁。