MySQL学习笔记(2)并发问题与事务隔离级别
本文参考:https://javaguide.cn/database/mysql/transaction-isolation-level.html
并发事务问题
脏读:一个事务读到了另一个事务中未提交的数据
不可重复读:一个事务先后读取同一条数据,当时两次获取到的结果不同
幻读:一个事务按照条件查询数据时,没有查到对应的数据行,当时在插入数据时,又发现这行数据已经存在
SQL中标准的四个隔离级别:
- 读取未提交(READ-UNCOMMITTED):允许读取尚未提交的数据变更。
- 读取已提交(READ-COMMITTED):允许读取并发事务已经提交的数据。
- 可重复读(REPEATABLE-READ):对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改。
- 可串行化(SERIALIZABLE):所有的事务依次逐个执行。
MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读),但是通过MVCC
机制和Next-Key Lock
机制防止了幻读现象的出现。
InnoDB 的 MVCC 机制
InnoDB 使用多版本并发控制(MVCC)来实现一致性读。MVCC 的核心思想是:
- 每个事务都有一个唯一的事务 ID(事务版本号)。
- 数据行会保存多个版本(通过隐藏的列 DB_TRX_ID 和 DB_ROLL_PTR 记录版本信息)。
- 事务读取数据时,会根据自己的事务 ID 看到数据在事务开始时的快照版本,而不是实时版本。
MVCC 如何防止幻读?
- 在 REPEATABLE-READ 级别下,事务 A 的快照版本是固定的,它只能看到在事务开始时已经存在的数据行。
- 即使其他事务插入了新行,这些新行不会出现在事务 A 的快照版本中,因此事务 A 不会看到这些新行,从而避免了幻读。
Next-Key Lock 机制
InnoDB 在 REPEATABLE-READ 级别下使用 Next-Key Lock 来进一步防止幻读。Next-Key Lock 是行锁(Record Lock)和间隙锁(Gap Lock)的结合:
- 行锁(Record Lock):锁定已存在的数据行。
- 间隙锁(Gap Lock):锁定数据行之间的间隙,防止其他事务插入新行。
Next-Key Lock 如何防止幻读?
- 当事务 A 对某个范围的数据进行读取时,InnoDB 会使用 Next-Key Lock 锁定整个范围(包括已存在的行和行之间的间隙)。
- 如果事务 B 尝试在这个范围内插入新行,会被间隙锁阻塞,直到事务 A 提交或回滚。
这样,事务 A 在整个事务期间都能保证范围内的数据不会被其他事务插入新行,从而防止了幻读。
不可重复读和幻读的区别是一个读取的一行,一个读取的是多行吗?
不可重复读和幻读的区别并不是由读取单行或多行决定的,而是由数据变化的类型决定的。以下是两者的详细区别:
1. 不可重复读(Non-Repeatable Read)
不可重复读是指在同一个事务中,两次读取同一行数据时,数据的内容发生了变化。这种现象通常是由于其他事务对该行数据进行了更新(UPDATE
)操作。
特点:
- 数据行不变:数据行的主键或唯一标识没有变化。
- 数据内容变化:数据行的内容(字段值)发生了变化。
- 解决方法:通过记录锁(Record Lock)锁定数据行,防止其他事务更新该行。
2. 幻读(Phantom Read)
幻读是指在同一个事务中,两次读取同一范围的数据时,数据的集合发生了变化。这种现象通常是由于其他事务在该范围内插入(INSERT
)或删除(DELETE
)了数据行。
特点:
- 数据范围不变:查询的条件范围没有变化。
- 数据集合变化:查询结果集中出现了新的行或某些行消失了。
- 解决方法:通过间隙锁(Gap Lock)或 Next-Key Lock(记录锁 + 间隙锁)锁定数据范围,防止其他事务在该范围内插入或删除数据。
3. 不可重复读 vs. 幻读
- 不可重复读:关注的是数据行的内容变化,通常是由于其他事务对同一行数据进行了更新操作。
- 幻读:关注的是数据范围内的集合变化,通常是由于其他事务在范围内插入或删除了数据。