数据库事务中的脏读、不可重复读、幻读
数据库事务中的脏读、不可重复读、幻读
这三个现象都是在多个事务并发执行时,由于隔离性控制不当而出现的问题。
1. 脏读
- 定义:一个事务读到了另一个尚未提交的事务修改的数据。
- 核心:读到了“脏”的、可能不存在的中间数据。
- 发生场景:事务隔离级别为 读未提交 时。
- 例子:
- 事务A 将某条记录的
余额
从 100 元 修改为 200 元(但尚未提交)。 - 此时,事务B 读取 这条记录,读到的
余额
是 200 元。 - 事务A 因为某种原因(如出错)回滚 了操作,
余额
恢复为 100 元。 - 结果:事务B 读到了一个根本不存在的数据(200元),这就是脏读。
- 事务A 将某条记录的
2. 不可重复读
- 定义:在同一个事务中,两次读取同一条数据,得到的结果不一致。
- 核心:针对的是同一条记录的更新操作。
- 发生场景:事务隔离级别为 读已提交 时,可以避免脏读,但可能出现不可重复读。
- 例子:
- 事务A 第一次读取某条记录的
余额
,得到 100 元。 - 此时,事务B 更新 了这条记录,将
余额
改为 200 元,并提交了事务。 - 事务A 再次读取同一条记录,此时读到的
余额
是 200 元。 - 结果:事务A 在同一个事务内,两次读取同一条数据,结果却不一致。这对于需要在事务内保持数据一致性的场景(如对账)是致命的。
- 事务A 第一次读取某条记录的
3. 幻读
- 定义:在同一个事务中,两次执行相同的查询,返回的结果集记录数量不一致。
- 核心:针对的是数据集的插入或删除操作,感觉像出现了“幻觉”一样的新数据。
- 发生场景:事务隔离级别为 可重复读 时,可以避免脏读和不可重复读,但可能出现幻读。
- 例子:
- 事务A 第一次查询
年龄 < 30
的员工,返回了 10 条记录。 - 此时,事务B 插入 了一条新的员工记录,其年龄为 25 岁,并提交了事务。
- 事务A 再次执行完全相同的
年龄 < 30
查询,此时返回了 11 条记录。 - 结果:事务A 感觉就像发生了“幻觉”,凭空多出来一条数据。
- 事务A 第一次查询
总结与对比
现象 | 核心问题 | 操作类型 | 一句话概括 |
---|---|---|---|
脏读 | 读到了未提交的数据 | 任何修改 | 读到了“即将可能不存在”的数据。 |
不可重复读 | 同一记录内容被修改 | UPDATE | 同一条数据,两次读的内容不一样。 |
幻读 | 结果集数量发生变化 | INSERT, DELETE | 同样的查询,两次读到的记录条数不一样。 |
与事务隔离级别的关系
SQL标准定义了4个事务隔离级别,用于解决上述问题:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交 | 可能发生 | 可能发生 | 可能发生 |
读已提交 | 不会发生 | 可能发生 | 可能发生 |
可重复读 | 不会发生 | 不会发生 | 可能发生 |
可序列化 | 不会发生 | 不会发生 | 不会发生 |
注意:在 MySQL 的 InnoDB 引擎中,通过 MVCC(多版本并发控制) 和 Next-Key Locking 机制,在 可重复读 隔离级别下就已经可以很大程度上避免幻读的发生。因此,MySQL 的“可重复读”比标准SQL定义的更严格。