数据库的隔离级别(Isolation Levels)是用来解决多个事务并发执行时,可能出现的互相干扰问题的。
为了权衡数据一致性和系统并发性能,SQL 标准定义了四个隔离级别。级别越高,数据越安全,但并发性能越差。
1. 并发事务会引发的三个“怪现象”
在了解隔离级别前,必须先知道它们要解决的“三大问题”:
-
脏读 (Dirty Read):事务 A 读取了事务 B 还没提交的数据。如果事务 B 随后回滚,事务 A 读到的就是过期且不存在的假数据。
-
不可重复读 (Non-repeatable Read):事务 A 多次读取同一条记录。在两次读取之间,事务 B 修改并提交了数据。导致事务 A 两次读到的内容不一样。
-
幻读 (Phantom Read):事务 A 按某个条件查询一批记录。在两次查询之间,事务 B 插入或删除了数据。导致事务 A 发现记录变多或变少了。
2. 四种隔离级别对比
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 性能 | 备注 |
| 读未提交 (Read Uncommitted) | ❌ 有 | ❌ 有 | ❌ 有 | 🚀 极高 | 基本不用,非常危险。 |
| 读已提交 (Read Committed) | ✅ 无 | ❌ 有 | ❌ 有 | 📈 高 | Oracle/PostgreSQL 的默认级别。 |
| 可重复读 (Repeatable Read) | ✅ 无 | ✅ 无 | ❌ 有* | 📊 中 | MySQL (InnoDB) 的默认级别。 |
| 串行化 (Serializable) | ✅ 无 | ✅ 无 | ✅ 无 | 🐢 极低 | 强制事务排队执行,完全没有并发。 |
*注:MySQL 的 InnoDB 引擎通过 Next-Key Lock 机制,在“可重复读”级别下其实已经很大程度上解决了幻读问题。
3. 深度解析:不同级别的实现原理
① 读已提交 (RC):每次都看最新的
每次执行 SELECT 语句时,数据库都会重新生成一个快照(Read View)。所以如果你在同一个事务里查两次,而中间有人改了数据,你两次看到的快照是不一样的。
② 可重复读 (RR):只看一眼,记一辈子
只有在事务开始后的第一次 SELECT 时生成快照。后续所有的查询都复用这同一个快照。这就是为什么别人怎么改,你查出来的结果都一样。
③ 串行化 (Serializable):排队走
它不再使用快照,而是给读到的每一行数据都加共享锁(S Lock)。如果有人想改数据,必须等你的事务结束。这就像大家排成一队过独木桥,效率非常低,但绝对安全。
4. 生产环境如何选择?
-
大多数互联网公司:选择 读已提交 (RC)。
-
原因:RC 配合乐观锁能应对绝大多数高并发场景,且不会像 RR 那样因为锁太多(间隙锁)导致死锁风险。
-
-
金融/计费系统:通常维持 可重复读 (RR) 或更高。
-
原因:对账务的一致性要求极高,不能容忍任何数据飘移。
-
5. 总结
隔离级别本质上是 “锁” 与 “版本快照” 的博弈:
-
锁:保证安全,但会互相等待。
-
快照 (MVCC):保证并发,各看各的,互不干扰。
既然聊到了隔离级别,你想了解一下在你的 Java 应用中,如何通过 Spring 的 @Transactional(isolation = Isolation.REPEATABLE_READ) 来灵活控制代码块的隔离级别吗?