目录
1、背景
为了满足数据库对数据的一致性、事务隔离性、高并发性能需求,就有了锁机制,InnoDB的锁机制是实现事务隔离性和并发控制的核心组件,接下来就来讲一下锁机制相关知识。
2、事务并发问题的三种场景
【1】读读场景
并发读不会有啥问题,这种场景不会对数据有啥影响。
【2】写写场景
并发写会产生脏写,也就是一个事务会修改另一个未提交事务修改过的结果,所以需要锁来进行并发控制,锁的结构如下:
其字段含义为:
字段 | 含义 |
---|---|
事务信息 | 代表这个锁结构属于哪个事务 |
is_waiting | false-成功获取到锁,可以修改数据;true-需要等待锁释放 |
【3】读写或写读场景
这种场景会带来脏读、幻读、不可重复读问题,不同隔离级别可能带来的问题如下:
隔离级别 | 可能带来的问题 |
---|---|
READ UNCOMMITTED | 脏读、不可重复读、幻读 |
READ COMMIT | 不可重复读、幻读 |
REPEATABLE READ | 幻读 |
SERIALIZABLE | 无问题 |
为了解决脏读、幻读、不可重复读的问题有两种方案:
方案一:读操作使用MVCC,写操作加锁。
方案二:读、写操作都加锁。
3、一致性读
事务利用MVCC进行的读取操作称为一致性读,所有普通的SELECT语句在读已提交、可重复读的隔离级别下都算是一致性读,一致性读不会对记录做加锁操作,其它事务可以修改记录。
4、锁定读
【1】共享锁和独占锁
在使用加锁方式解决并发问题时,要保证读读操作不受影响,写写、读写、写读操作相互阻塞,所以将锁分为了两类,共享锁和独占锁,其含义为:
名称 | 含义 |
---|---|
共享锁 | 简称S锁,事务读取一条记录时,需要先获取该记录的锁 |
独占锁 | 也叫排他锁,简称X锁,在事务改动一条记录时,需要先获取该记录的X锁 |
【2】锁定读的两种语句
对读取的记录加s锁:
SELECT … LOCK IN SHARE MODE;
对读取的记录加x锁:
SELECT … FOR UPDATE;
5、写操作
DELETE:
对一条记录做DELETE操作的过程是先获取该记录在B+树中的位置,然后获取这条记录的x锁,再执行delete mark操作。我们可以把在B+树中找这条记录的过程看成一个获取x锁的锁定读。
UPDATE:
1、如果未修改该记录的键值并且被更新的列占用的存储空间在修改前后未发生变化,就先在B+树中定位这条记录的位置,然后再获取一下记录的x锁,最后在原位置进行修改操作。可以把这个在B+树中定位修改记录的过程看出有个获取x锁的锁定读。
2、如果未修改该记录的键值并且至少有一个被更新的列占用的存储空间在修改前后发生了变化,就先在B+树中获取这条记录的位置,然后获取这条记录的x锁,将该记录彻底删掉也就是移入垃圾链表,最后再插入一条新记录,这个在B+树中找到要删除的记录的过程可以看成一个获取x锁的锁定读,新插入的记录由INSERT操作提供的隐式锁进行保护。
3、如果修改了该记录的键值,相当于在原记录上做DELETE操作再来一次INSERT操作,加锁规则就按照DELETE和INSERT的规则进行。
INSERT:
通过隐式锁来保护插入的这条记录不被其它事务访问。
6、InnoDB中的表级锁
【1】表级别的S锁、X锁
在对某个表执行SELECT、INSERT、DELETE、UPDATE语句时,InnoDB存储引擎是不会为这个表添加表级别的s锁或x锁的。对某个表执行ALTER TABLE、DROP TABLE这类DDL语句时和SELECT、INSERT、DELETE、UPDATE语句是相互阻塞的,这个阻塞是通过server层中的元数据锁(简称MDL)来实现的。
【2】表级别的IS锁、IX锁
IS锁和IX锁的含义如下:
名称 | 含义 |
---|---|
IS锁 | 意向共享锁,当事务准备在某条记录上加S锁时,需先在表级别加一个IS锁 |
IX锁 | 意向独占锁,当事务准备在某条记录上加X锁时,需先在表级别加一个IX锁 |
【3】表级别的AUTO-INC锁
主键自增id,也就是带AUTO_INCREMENT属性的列就使用AUTO-INC锁。
7、InnoDB中的行级锁
【1】LOCK_REC_NOT_GAP行锁类型
这个类型锁分为s锁和x锁,和我们前面讲的共享锁和独占锁一样。
【2】LOCK_GAP行锁类型
这个类型的锁会阻塞当前加锁的这条记录和上一条记录之间的间隙插入。
【3】LOCK_ORDINARY行锁类型
这个类型的锁会保护当前被锁住的记录,并且阻塞这条记录和上一条记录的间隙插入。
【4】Insert Intention Locks行锁类型
被间隙阻塞插入的记录就会有有这样一个类型的锁结构。
【5】隐式锁
保护事务插入时的并发问题。
8、总结
InnoDB锁机制的背景是在保护事务隔离性的前提下最大化并发性,通过MVCC、行锁、表锁的协同,满足高并发场景需求。