持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第18天,点击查看活动详情
1. 事务与隔离级别
MySQL的锁机制是与其事务隔离级别紧密相关的。
对于MySQL而言,有四种隔离级别:
- 读未提交
- 读已提交:解决事务间的脏读问题
- 可重复读(默认):解决事务的不可重复度问题,部分解决幻读问题
- 序列化:串型化,不存在并发问题
为了达到以上隔离级别的要求,我们需要借助MySQL的锁机制来解决并发问题:
在读未提交的状态,读操作不加锁,所以事务A会读到事务B修改的数据;在读已提交的状态,因为读操作加了S锁,所以会被其他事务的写操作阻塞,进而避免了脏读的现象;可重复读的隔离级别,读操作会阻塞其他事务的写操作;序列化的隔离级别,各种操作顺序进行。
对于读已提交以及可重复度隔离级别下的读操作。MySQL提供了性能更好的MVCC快照读的机制
读已提交的隔离级别,每一次读取都会生成一份当前的快照,可重复读的隔离级别下,事务开始时会生成一份快照,之后一直在这份快照中读取。
2. MySQL中锁的种类
MySQL中的锁可以分为表锁与行锁两大类
- 表锁(Server层)
- 读锁(S)整张表的读锁
- 写锁(X)整张表的写锁
- 读意向锁(IS)标记表里面有行的读锁
- 写意向锁(IX)标记表里面有行的写锁
- 行锁(InnoDB层)
- 记录锁(REC_NOT_GAP)最简单的行锁
- 间隙锁(GAP)锁住一段区域的锁
- Next-Key锁(REC)索引上的记录锁和索引之前的间隙上的间隙锁的组合。
- 插入意向锁(INSERT——INTENSION)新插入的元素加的锁
3. 几种索引加锁情况详解
需要注意的是,一般的select操作会使用快照读,并不会加锁,select for update才会触发锁的机制,一般情况下,能不使用锁,就尽量少的使用锁操作。
- 在读已提交的隔离级别下,对于
select操作,会在二级索引以及聚簇索引上同时加锁。
- 对于
update操作,会根据where条件依次读取记录并进行加锁解锁操作,直至操作全部完成:
-
需要注意的是,对于读已提交隔离级别下的
insert操作,情况就会变得复杂很多,对于非唯一索引,可以用行锁解决问题,但是如果需要上锁的索引是唯一索引,就需要考虑唯一索引可能的冲突,因此需要使用间隙锁,新插入的元素会加上插入意向锁。 -
在可重复读的隔离级别下,除了等值查询的情况,加的锁都会是间隙锁。