数据库的脏读、不可重复读和幻读分别是什么?

0 阅读5分钟

在数据库的并发操作中,事务隔离级别是确保数据一致性的关键机制。而脏读、不可重复读和幻读,正是不同隔离级别下可能出现的典型问题。理解这些概念,对优化数据库事务管理至关重要。

一、脏读(Dirty Read)

定义:一个事务读取到另一个事务未提交的数据。若未提交事务最终回滚,第一个事务读取的数据就会变得不一致(即"脏"数据)。

示例:假设事务A读取某商品库存为100,此时事务B开始修改库存为90,但未提交。若事务A在事务B提交前读到了未提交的90,而事务B因某种原因回滚,库存恢复为100。此时事务A读取的90就是无效的脏数据,导致业务逻辑错误。

脏读示意图

二、不可重复读(Non-repeatable Read)

定义:在同一事务中,两次读取同一数据时结果不同。这是因为其他事务修改并提交了该数据,导致原事务两次读取结果不一致。

示例:事务A查询某用户余额为1000元,准备进行后续操作。此时事务B修改该用户余额为800元并提交。当事务A再次查询余额时,得到800元,两次结果不同。这种情况聚焦于数据内容的变化,影响事务内数据的一致性。

不可重复读示意图

image.png

三、幻读(Phantom Read)

定义:在同一事务中,执行相同查询操作,结果集却因其他事务插入新数据而变化。它关注的是数据总量的变化

示例:事务A查询所有订单金额大于1000元的订单,得到10条记录。此时事务B插入一条金额为1500元的订单并提交。当事务A再次执行相同查询时,结果变为11条。新插入的记录如同"幻影"般出现,干扰了事务A的查询结果。

幻读示意图

四、不可重复读与幻读的区别

不可重复读幻读
关注点数据内容的变化数据集合数量的变化
操作类型UPDATE 导致数据内容变更INSERT/DELETE 导致数据行数变化
影响范围针对已有数据行影响查询结果集
示意图

五、隔离级别与三种问题的关系

隔离级别脏读不可重复读幻读性能影响
读未提交 (Read Uncommitted)✓ 允许✓ 允许✓ 允许最小
读已提交 (Read Committed)✗ 防止✓ 允许✓ 允许较小
可重复读 (Repeatable Read)✗ 防止✗ 防止✓ 允许*中等
串行化 (Serializable)✗ 防止✗ 防止✗ 防止最大
  • 注意:MySQL的InnoDB引擎在可重复读级别下通过间隙锁(Gap Lock)机制解决了部分幻读问题。

实际应用场景与选择策略

不同业务场景的隔离级别选择

业务场景推荐隔离级别原因
电商库存管理可重复读或串行化防止超卖问题,保证库存计算准确
银行账户转账可重复读或串行化确保资金安全,防止转账错误
日志记录系统读未提交或读已提交性能要求高,数据一致性要求相对较低
报表统计分析读已提交通常只需要数据大致准确,对实时性要求不高
订单处理系统可重复读需要保证订单数据一致性,避免处理错误

image.png

主流数据库默认隔离级别

数据库系统默认隔离级别特殊说明
MySQL (InnoDB)可重复读 (REPEATABLE READ)通过MVCC和间隙锁部分解决幻读问题
PostgreSQL读已提交 (READ COMMITTED)通过MVCC实现,可升级到可串行化
SQL Server读已提交 (READ COMMITTED)支持快照隔离级别
Oracle读已提交 (READ COMMITTED)通过MVCC机制实现
SQLite串行化 (SERIALIZABLE)默认最高隔离级别

示例代码:不同隔离级别下的行为测试

MySQL中设置和测试隔离级别

-- 设置会话隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
-- 或 READ COMMITTED / REPEATABLE READ / SERIALIZABLE

-- 测试脏读的示例代码(在两个会话窗口中执行)
-- 会话1:
START TRANSACTION;
UPDATE accounts SET balance = 500 WHERE id = 1;
-- 不提交

-- 会话2 (READ UNCOMMITTED):
START TRANSACTION;
SELECT balance FROM accounts WHERE id = 1;
-- 会读到500(脏读)
COMMIT;

-- 会话1:
ROLLBACK;
-- 数据恢复为原值

性能与数据一致性的平衡

在选择隔离级别时,需要在性能和数据一致性之间做出权衡:

性能开销随隔离级别提高而增加:

  1. 读未提交:几乎没有额外锁开销,但数据一致性最差
  2. 读已提交:需要读锁,但释放较快
  3. 可重复读:需维护更多锁或版本信息
  4. 串行化:锁定范围最广,并发度最低

最后

理解脏读、不可重复读和幻读,能帮助我们根据业务需求选择合适的隔离级别,在数据一致性和性能间找到平衡。实际开发中,需结合场景评估:如电商库存扣减需防止脏读,而某些统计类查询对幻读的容忍度可能较高。

合理运用这些知识,可有效提升数据库并发处理的稳定性与可靠性。数据库事务隔离是一门平衡的艺术,既要满足业务对数据一致性的需求,又要在高并发场景下保持良好的性能表现。