READ COMMITTED 能防止脏读,因其只允许读取已提交数据,通过 MVCC 为每次 SELECT 构建基于最新已提交版本的快照,跳过未提交修改,从而天然拦截脏读。

READ COMMITTED 级别本身就能避免脏读,不需要额外“避免”——它通过只允许读取已提交的数据来天然拦截脏读。
为什么 READ COMMITTED 能防止脏读
脏读的本质是:事务 A 修改了数据但未提交,事务 B 读到了这个未提交的中间状态;若 A 回滚,B 就读到了根本不存在的“脏数据”。
READ COMMITTED 的实现机制(如 MySQL InnoDB 的 MVCC)会为每次 SELECT 构建一个基于最新已提交版本的快照,跳过所有未提交的修改。所以事务 B 永远看不到 A 那个还没落地的 5000 元工资,只会等到 A 提交 2000 元后才读到它。
Java 中正确启用 READ COMMITTED
仅设置隔离级别还不够,需确保它真正生效:
- 使用 JDBC 原生方式:调用 connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED),且必须在 开启事务之后、执行 SQL 之前 设置;
- Spring 中通过 @Transactional(isolation = Isolation.READ_COMMITTED) 声明,注意该注解只对被 Spring 代理的方法生效;
- 确认数据库实际支持并默认启用该级别——例如 PostgreSQL 默认就是 READ COMMITTED,而 MySQL 默认是 REPEATABLE READ,需显式覆盖;
- 避免连接池复用导致隔离级别残留:某些连接池(如 HikariCP)不会自动重置隔离级别,建议在获取连接后显式设置,或配置 connection-init-sql 自动初始化。
常见误区:以为设了就万事大吉
以下情况会让 READ COMMITTED 失效或看似“没防住脏读”:
立即学习“Java免费学习笔记(深入)”;
- 代码中手动执行了 SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED 这类语句;
- 事务未真正开启(比如 auto-commit=true 时,每条 SQL 是独立事务,隔离级别设置无效);
- 跨库或跨服务调用时,下游系统用了更低隔离级别,上游的 READ COMMITTED 无法约束它;
- 误把不可重复读或幻读当成脏读——例如两次查询余额不一致,其实是别的事务已提交了转账,这属于正常行为,不是脏读。
验证是否真的避免了脏读
可设计简单双线程测试:
- 线程 A 启动事务、更新某行、sleep 2 秒、然后 rollback;
- 线程 B 在 A sleep 期间执行 SELECT;
- 若 B 查到的是旧值(而非 A 中途改的值),说明脏读已被阻止。


















