MySQL InnoDB存储引擎 之 锁

xiaoxiao2021-07-27  144

MySQL InnoDB存储引擎有七种锁

自增锁(Auto-inc Locks)共享/排他锁(Shared and Exclusive Locks)意向锁(Intention Locks)插入意向锁(Insert Intention Locks)记录锁(Record Locks)间隙锁(Gap Locks)临键锁(Next-Key Locks)

1. 自增锁(Auto-inc Locks)

1.1 自增实现

在InnoDB存储引擎内存结构中,对每个含有自增值的表都有一个自增长计数器,当对其进行插入操作时,计数器会被初始化,执行如下语句得到计数器值:

SELECT MAX(auto_inc_col) FROM t FOR UPDATE;

可以看到该SQL语句后面加了排它锁(FOR UPDATE),为了提高插入性能,这个锁不是在事务完成后才释放,而是完成对自增长值插入的SQL语句后就立即释放。

上面的方式称为传统的AUTO-INC Locking方式,它其实采用的是一种特殊的表锁机制,所以对于大量的数据的并发插入还是会有性能问题,试想一个事务执行insert…select的大量数据插入,另外一个事务的插入会被阻塞。

1.2 innodb_autoinc_lock_mode参数

从MySQL5.1.22版本开始,InnoDB存储引擎提供了一个参数innodb_autoinc_lock_mode,默认值为1。讨论该值的意义前,先对自增插入进行分类:

INSERT-like: 指所有插入语句,如:insert、replace、insert…select、replace…select、load data等。Simple inserts:指在出入前就确定插入行数的语句,如:insert、replace。Bulk inserts:指在出入前不能确定插入行数的语句,如insert…select、replace…select、load data。Mixed-mode inserts:指插入中有一部分的值时自增,有一部分是确定的。

了解上面四种插入类型后,现在讨论innodb_autoinc_lock_mode参数的值的情况:

innodb_autoinc_lock_mode=0: 5.1.22版本之前自增的实现方式innodb_autoinc_lock_mode=1: 默认值。对于“Simple inserts”,会用互斥量去对内存中的计数器进行累加操作。对于“Bulk inserts”采用传统的AUTO-INC Locking方式。如果已经使用了AUTO-INC Locking方式,再进行“Simple inserts”操作,还是要等到AUTO-INC Locking释放。innodb_autoinc_lock_mode=2: 对于所有“INSERT-like”都是使用互斥量产生自增值。

PS: MyISAM引擎的自增长是表锁;InnoDB引擎下,自增列必须是索引,并且是索引第一列。

2. 共享/排他锁(Shared and Exclusive Locks)

共享锁(Share Locks,记为S锁): 读取数据时加S锁

排他锁(eXclusive Locks,记为X锁): 修改数据时加X锁

S X S 兼容 互斥 X 互斥 互斥

即:

(1)多个事务可以拿到一把S锁,读读可以并行;

(2)而只有一个事务可以拿到X锁,写写/读写必须互斥;

InnoDB支持多粒度锁(multiple granularity locking),它允许行级锁与表级锁共存,实际应用中,InnoDB使用的是意向锁。

意向锁是指,未来的某个时刻,事务可能要加共享/排它锁了,先提前声明一个意向。

3. 意向锁(Intention Locks)

意向锁有这样一些特点:

(1)首先,意向锁,是一个表级别的锁(table-level locking);

(2)意向锁分为:

意向共享锁(intention shared lock, IS),它预示着,事务有意向对表中的某些行加共享S锁意向排它锁(intention exclusive lock, IX),它预示着,事务有意向对表中的某些行加排它X锁

意向锁协议(intention locking protocol)并不复杂:

事务要获得某些行的S锁,必须先获得表的IS锁

事务要获得某些行的X锁,必须先获得表的IX锁

举个例子:

select ... lock in share mode,要设置IS锁; select ... for update,要设置IX锁;

插入意向锁(Insert Intention Locks)

对已有数据行的修改与删除,必须加强互斥锁X锁,那对于数据的插入,使用插入意向锁。

插入意向锁,是间隙锁(Gap Locks)的一种,专门针对insert操作的。多个事务,在同一个索引,同一个范围区间插入记录时,如果插入的位置不冲突,不会阻塞彼此。


— 锁算法 —

5. 记录锁(Record Locks)

记录锁,它封锁索引记录,例如:

select * from t where id=1 for update;

它会在id=1的索引记录上加锁,以阻止其他事务插入,更新,删除id=1的这一行

需要说明的是:

select * from t where id=1;

则是快照读(SnapShot Read),它并不加锁。

6. 间隙锁(Gap Locks)

间隙锁,它封锁索引记录中的间隔,或者第一条索引记录之前的范围,又或者最后一条索引记录之后的范围。

select * from t where id between 8 and 15 for update;

会封锁区间[8,15],以阻止其他事务把id=10的记录插入。

间隙锁的主要目的,就是为了防止其他事务在间隔中插入数据,以导致“不可重复读”。如果把事务的隔离级别降级为读提交(Read Committed, RC)及以下,间隙锁则会自动失效。

7. 临键锁(Next-Key Locks)

临键锁,是记录锁与间隙锁的组合,它的封锁范围,既包含索引记录,又包含索引区间。

更具体的,临键锁会封锁索引记录本身,以及索引记录之前的区间。

临键锁的主要目的,是为了避免幻读(Phantom Read)。如果把事务的隔离级别降级Read Committed及以下,临键锁则也会失效。


PS:

对于外键列,InnoDB引擎会自动对其添加一个索引,这样可以避免死锁,而且不能手动删除。InnoDB引擎中,事务阻塞时,innodb_lock_wait_timeout字段可用来控制等待时间(默认50秒)。innodb_rollback_on_timeout用来设定是否在等地超时时对进行中的事务进行回滚(默认为OFF,代表不回滚)。需要注意的是,InnoDB不会回滚超时引发的异常。死锁(1213错误)发生后,InnoDB会马上回滚一个事务。InnoDB不会回滚大部分的错误异常,死锁除外。InnoDB存储引擎不存在锁升级问题,因为1个锁开销与100000个锁是一样的,都没有开销。

参考:

《MySQL技术内幕:InnoDB存储引擎》

关于MySQL内核,一定要知道的!

转载请注明原文地址: https://www.6miu.com/read-4823356.html

最新回复(0)