InnoDB 锁类型
表锁是对一整张表加锁,虽然可分为读锁和写锁,但毕竟是锁住整张表,会导致并发能力下降,一般是做ddl处理时使用。
行锁则是锁住数据行,这种加锁方法比较复杂,但是由于只锁住有限的数据,对于其它数据不加限制,所以并发能力强,MySQL一般都是用行锁来处理并发事务。这里主要讨论的也就是行锁。
-
共享锁
Shared Locks(简称S 锁,属于行锁) -
排他锁
Exclusive Locks(简称X 锁,属于行锁) -
意向共享锁
Intention Shared Locks(简称IS 锁,属于表锁) -
意向排他锁
Intention Exclusive Locks(简称IX 锁,属于表锁) -
自增锁
AUTO-INC Locks
共享锁
又称之为 读 锁,简称 s 锁,顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据库,但是只能读不能修改;
select * from users where id = 1 lock in share mode;
排它锁
又称为写锁,简称 X 锁,排它锁不能与其他锁并存,如一个事务获取了一个数据行的排它锁,其他事务就不能再获取改行的锁(包括共享锁和排它锁),只有当前获取了排它锁的事务可以对数据进行读取和修改(此时其他事务要读取数据可从快照获取)
select * from users where id = 1 for update;
意向锁
InnoDB 支持多粒度的锁,允许表级锁和行级锁共存。
- 一个事务必须先持有该表上的
IS 或者更强的锁才能持有该表中某行的S 锁 - 一个事务必须先持有该表上的
IX 锁才能持有该表中某行的X 锁
意向锁是InnoDB数据操作之前自动加的,不需要用户干预。
当事务想去进行锁表时,可以先判断意向锁是否存在,存在时则可快速的返回,告知该表不能启用表锁(也就是会锁住对应会话),提高了加锁的效率。
自增锁
自增锁是一个特殊的表级锁,事务插入自增列的时候需要获取,最简单情况下如果一个事务插入一个值到表中,任何其他事务都要等待,这样第一个事物才能获得连续的主键值。
innodb_autoinc_lock_mode配置选项控制了自增锁的算法,它让你选择在可预测的连续自增值和并发度之间的平衡。
锁的兼容
| X | IX | S | IS | |
|---|---|---|---|---|
| X | Conflict | Conflict | Conflict | Conflict |
| IX | Conflict | Compatible | Conflict | Compatible |
| S | Conflict | Conflict | Compatible | Compatible |
| IS | Conflict | Compatible | Compatible | Compatible |
可重复读 REPEATABLE-READ(RR)
InnoDB默认的RR事务隔离级别下,不显式加lock in share mode与for update的select操作都属于快照读,保证事务执行过程中只有第一次读之前提交的修改和自己的修改可见,其他的均不可见;
行锁的算法
行锁锁的是索引上的索引项,InnoDB所有的行锁算法都是基于索引实现的,锁定的也都是索引或索引区间。
只有通过索引条件进行数据检索,Innodb才使用行级锁;否则,将使用表锁(锁住索引的所有记录)。
记录锁 Record Locks
记录锁针对索引记录。
# e.g.
SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE;
间隙锁 Gap Locks
间隙锁(gap)是索引记录之间上的锁,或者说第一个索引记录之前或最后一个索引记录之后的间隔上的锁。
# e.g.
SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE;
临键锁 Next-Key Locks
Next-Key Locks (简称 NK 锁)是记录锁和间隙锁的组合。
当SQL执行按照索引进行数据的检索时,查询条件为范围查找(between and < > 等等)并有数据命中,则测试SQL语句加上的锁为Next-Key locks,锁住索引的记录区间加下一个记录区间,这个区间是左开右闭 -> (x,y]的
假设一个索引包含值 10,11,13和20,索引上可能的NK 锁包括如下几个区间(注意开闭区间)
(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)
默认情况下,Innodb 是RR级别,这样的话,Innodb使用NK 锁来进行索引搜索和扫描,阻止了幻读。
MySQL 的 Innodb引擎正是通过上述不同类型的锁,完成了事务隔离:
- 加
X 锁避免了数据的脏读 - 加
S 锁避免了数据的不可重复读 - 加上
Next-Key锁避免了数据的幻读