🔗 点击获取《脏读问题排查手册》(提取码:9b3g)

什么是数据库脏读?
脏读(Dirty Read)是数据库事务隔离级别中的经典问题。当事务A读取了事务B未提交的数据,而事务B随后发生回滚,此时事务A读到的就是无效的"脏数据"。例如:
- 事务B修改某商品库存为90(原为100)
- 事务A读取到库存90
- 事务B因异常回滚,库存恢复为100
- 事务A后续基于90的"脏数据"进行业务操作

脏读产生的三大技术原因
事务隔离级别设置为READ UNCOMMITTED
这是直接导致脏读的配置。MySQL默认隔离级别为REPEATABLE READ,但若显式设置为:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
其他事务的未提交修改会立即可见。
缺乏行级锁机制
在以下场景可能发生:
- 事务B开启并修改数据(未提交)
- 事务A直接读取数据(不加锁)
- 事务B回滚
若事务A使用SELECT ... FOR UPDATE加锁,则可避免读取未提交数据。
跨事务可见性设计缺陷
某些数据库中间件或ORM框架的缓存机制若未正确处理事务边界,可能将未提交数据存入缓存,导致其他事务读取到过期数据。
如何精准避免脏读?
调整事务隔离级别
将隔离级别提升至READ COMMITTED及以上。以MySQL为例:
SET GLOBAL TRANSACTION_ISOLATION = 'READ-COMMITTED';
该级别下,事务只能读取已提交的数据。
合理使用锁机制
BEGIN;
SELECT stock FROM products WHERE id=1 FOR UPDATE; -- 加排他锁
UPDATE products SET stock=90 WHERE id=1;
COMMIT;
通过FOR UPDATE锁定读取行,其他事务需等待锁释放才能读取。
框架层解决方案
在Spring框架中可通过声明式事务管理:
@Transactional(isolation = Isolation.READ_COMMITTED)
public void updateStock() {
// 业务逻辑
}

面试应答技巧
面试官:说说数据库脏读的原因和解决方法?
推荐回答:
脏读本质是事务隔离性被破坏,核心原因有三点:
- 事务隔离级别设置为READ UNCOMMITTED
- 缺乏必要的行锁机制
- 数据库/中间件对事务可见性处理不当
解决方案建议分三层处理:
- 数据库层:调整隔离级别到READ COMMITTED
- 应用层:合理使用SELECT...FOR UPDATE
- 架构层:通过二阶段提交等分布式事务方案


