数据库锁原理
- 锁的定义
- InnoDB中的锁模式
- 共享锁
- 独占锁
- 共享意向锁和独占意向锁
- LOCK_AUTO_INC自增锁
- INNODB_AUTOINC_LOCK_MODE
- InnoDB中的锁类型
- 表锁
- 行锁
- 行子类型
- LOCK_REC_NOT_GAP精准行锁
- LOCK_GAP行GAP锁
- LOCK_ORDINARY行NEXT-KEY锁
- LOCK_INSERT_INTENTION插入意向锁
锁的定义
为了体内的事务和事务之间相互不影响而进化出来的功能。
在某个事务操作数据之前,为这些数据加上一把锁,防止其他事务同时操作,在操作完成之后会将锁释放。以便其他事务再次进行上锁执行。
不同的存储引擎中,锁功能也并不相同。
InnoDB中的锁模式
共享锁
可以使用LOCK IN SHARE MODE为数据添加共享锁。
数据加了共享锁之后,其他事务仍然可以加新的共享锁。但无法添加排他锁。这样就可以在读取数据的时候,阻止数据被其他事务修改。
独占锁
独占锁,也就是排它锁或写锁,可以通过FOR UPDATE添加。数据加了独占锁之后,直到被释放之前,其他事务将不能再为数据加任何锁。
合理使用共享锁独占锁就可以解决事务间写入隔离的问题,以及脏读、重复读。
不过有关读的问题可以使用效率更高的MVCC解决。
共享意向锁和独占意向锁
也称为IS共享意向锁和IX独占意向锁。
意向锁就是在上锁时需要在数据所在表上做一个标记,表示这个表已经被加锁。
这样的好处是其他事务为这个表加锁之前,就不需要遍历每一条数据中是否有锁。而只需要看一下表的意向锁即可。大大提升锁判断的效率。
LOCK_AUTO_INC自增锁
LOCK_AUTO_INC自增锁是一个表锁。
配置了AUTO_INCREMENT的自增列服务。
在插入数据时会增加自增锁,然后生成自增值。同时阻塞其他的插入操作,以保证值的唯一。
值得注意的是,如果自增值已经生成,但事务回滚了,自增值也不会回退。也就是说明自增值可能不连续。
INNODB_AUTOINC_LOCK_MODE
通过INNODB_AUTOINC_LOCK_MODE来设置自增锁的机制。
0指自增场景都增加自增锁。
2指自增场景不添加锁直接生成自增值。
这样即使不回滚,在并发插入情况下也会出现不连续。
1默认是在插入数量时采用2机制,而不确定数量时使用0机制。
InnoDB中的锁类型
LOCK_TYPE按照锁的粒度分为:
- LOCK_TABLE(表锁)
- LOCK_REC(行锁)
在BDB存储引擎中还支持页锁
表锁
表锁指对整张表加锁。
在释放之前拒绝其他事务操作这张表的任何数据。
在特有信息中会记录被锁的表信息。
行锁
对表内某些行进行加锁。
在特有信息中会记录被锁表的信息以及对应的表空间和页号。
最重要的是会使用一个BIT数组记录哪些行被锁了。
行锁都是添加到MySQL中的数据或者索引中,也就是聚簇索引树或者二级索引树的行中。
根据不同的查询条件,会选择不同功能的行锁。这就要看一下行子类型。
行子类型
行子类型REC_LOCK_TYPE包括4种:
- LOCK_REC_NOT_GAP精准行锁
- LOCK_GAP行GAP锁
- LOCK_ORDINARY行NEXT-KEY锁
- LOCK_INSERT_INTENTION插入意向锁
LOCK_REC_NOT_GAP精准行锁
精准行锁只锁住精准对应的行。
LOCK_GAP行GAP锁
行GAP锁,锁住行与行之间的间隙。防止在行间隙中插入数据。
LOCK_ORDINARY行NEXT-KEY锁
行NEXT-KEY锁,即是精准行锁和行GAP锁的组合。
直观来看,这三种行锁的范围会根据不同的WHERE条件进行选择。
LOCK_INSERT_INTENTION插入意向锁
最后插入意向锁是一个特殊的GAP锁,也是共享锁。
如果一个行间隙加了GAP锁,这时其他事务如果要在这个行间隙插入数据,就会加插入意向锁。
由于是共享的,所以多个事务都可以加这个锁。
当GAP锁释放之后,MySQL允许多个插入意向锁同时进行,不需要串行执行,以提升插入效率。
参考资料:5分钟精通数据库锁原理-事务的隔离性