事务隔离级别详解
1. 事务并发问题
1. 脏读(Dirty Read)
-
定义:事务A读取了事务B未提交的数据,事务B回滚后,事务A读取的数据无效。
-
示例:
-- 事务A UPDATE users SET balance = 500 WHERE id = 1; -- 未提交 -- 事务B SELECT balance FROM users WHERE id = 1; -- 读取到500(脏数据) -- 事务A回滚后,事务B读到的数据无效。
2.不可重复读(Non-Repeatable Read)
-
定义:事务A多次读取同一数据,事务B在期间修改并提交了该数据,导致事务A前后读取结果不一致。
-
示例:
-- 事务A SELECT balance FROM users WHERE id = 1; -- 结果为100 -- 事务B UPDATE users SET balance = 200 WHERE id = 1; -- 提交 -- 事务A再次读取 SELECT balance FROM users WHERE id = 1; -- 结果为200(不一致)
3.幻读(Phantom Read)
-
定义:事务A查询某个范围的数据,事务B在期间插入或删除符合该范围的数据,导致事务A两次查询结果的行数不同。
-
示例:
-- 事务A SELECT * FROM users WHERE age > 18; -- 返回2行 -- 事务B INSERT INTO users (id, age) VALUES (3, 20); -- 提交 -- 事务A再次查询 SELECT * FROM users WHERE age > 18; -- 返回3行(出现幻行)
2. 事务隔离级别
由低到高分为四个级别,解决的问题逐步严格:
隔离级别 | 脏读 | 不可重复读 | 幻读 | 实现机制 | 性能 |
---|---|---|---|---|---|
读未提交 (Read Uncommitted) | ❌ 允许 | ❌ 允许 | ❌ 允许 | 无锁或读不加锁 | 最高 |
读已提交 (Read Committed) | ✅ 避免 | ❌ 允许 | ❌ 允许 | 行级共享锁(读时加锁,读完释放) | 高 |
可重复读 (Repeatable Read) | ✅ 避免 | ✅ 避免 | ⚠️ 部分避免(如MySQL) | 快照读(MVCC) + Next-Key锁 | 中 |
串行化 (Serializable) | ✅ 避免 | ✅ 避免 | ✅ 避免 | 表级锁或严格范围锁 | 最低 |
3. 各隔离级别实现原理
1.读未提交
-
实现:不采用任何锁机制,直接读取最新数据(包括未提交的数据)。
-
问题:所有并发问题均存在。
2.读已提交
-
实现(以Oracle为例):
-
写操作:事务修改数据时加排他锁(X锁),直到事务结束。
-
读操作:每次读取时获取共享锁(S锁),读完立即释放。
-
-
示例:
-- 事务A修改数据(加X锁) UPDATE users SET balance = 500 WHERE id = 1; -- 事务B读取时需等待事务A释放X锁,避免脏读。
3.可重复读
-
实现(以MySQL的InnoDB为例):
-
MVCC(多版本并发控制):为每个事务生成数据快照,保证多次读取一致性。
-
Next-Key锁:行锁 + 间隙锁,防止其他事务在范围内插入新数据(解决幻读)。
-
-
示例:
-- 事务A首次读取(生成快照) SELECT * FROM users WHERE age > 18; -- 返回2行 -- 事务B插入新数据(被Next-Key锁阻塞) INSERT INTO users (id, age) VALUES (3, 20); -- 等待 -- 事务A再次读取(仍为2行) SELECT * FROM users WHERE age > 18;
4.串行化
-
实现:所有操作按顺序执行,通过表级锁或严格的范围锁实现。
-
问题:并发性能极低,仅适用于低并发场景。
4. 数据库实现差异
1.MySQL(InnoDB引擎)
-
默认隔离级别:可重复读(Repeatable Read)。
-
幻读处理:通过Next-Key锁避免幻读。
2.Oracle / PostgreSQL
-
默认隔离级别:读已提交(Read Committed)。
-
幻读处理:在可重复读级别下仍可能出现幻读,需升级到串行化。
3.SQL Server
-
默认隔离级别:读已提交(Read Committed)。
-
支持快照隔离级别(Snapshot Isolation):类似MVCC,避免不可重复读和幻读。
5. 隔离级别设置
-
SQL语句设置:
-- 设置当前会话的隔离级别 SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; -- 开始事务 START TRANSACTION;
2.JDBC连接设置(Java示例):
Connection conn = dataSource.getConnection();
conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
3.Spring框架配置:
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void updateUser() {
// 业务逻辑
}
6. 实际应用建议
-
读已提交:适用于多数OLTP场景,平衡性能与一致性。
-
可重复读:需要事务内多次读取一致的场景(如对账系统)。
-
串行化:仅用于关键数据(如金融交易),确保绝对一致。
总结:事务隔离级别通过锁机制或MVCC解决并发问题,需根据业务需求与数据库特性选择。理解不同级别的行为差异,是设计高并发系统的关键基础。