MySQL 事务隔离级别详解
事务隔离级别是数据库管理系统用来控制事务间相互影响程度的重要机制。MySQL支持四种标准的事务隔离级别,每种级别在数据一致性和并发性能之间提供不同的平衡。
一、四种事务隔离级别
MySQL支持以下四种标准隔离级别(从低到高):
- READ UNCOMMITTED (读未提交)
- READ COMMITTED (读已提交)
- REPEATABLE READ (可重复读) - MySQL默认级别
- SERIALIZABLE (可串行化)
二、各隔离级别详解
1. READ UNCOMMITTED (读未提交)
特点:
- 事务可以读取其他事务未提交的修改(“脏读”)
- 最低的隔离级别
- 性能最好,但数据一致性最差
可能问题:
- 脏读(Dirty Read)
- 不可重复读(Non-repeatable Read)
- 幻读(Phantom Read)
示例:
-- 会话A
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- 尚未提交
-- 会话B
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
SELECT balance FROM accounts WHERE id = 1; -- 能看到A未提交的修改
2. READ COMMITTED (读已提交)
特点:
- 只能读取已提交的数据
- 解决了脏读问题
- Oracle等数据库的默认级别
可能问题:
- 不可重复读
- 幻读
示例:
-- 会话A
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
SELECT balance FROM accounts WHERE id = 1; -- 返回1000
-- 会话B
UPDATE accounts SET balance = 900 WHERE id = 1;
COMMIT;
-- 会话A再次查询
SELECT balance FROM accounts WHERE id = 1; -- 返回900(不可重复读)
3. REPEATABLE READ (可重复读) - MySQL默认
特点:
- 同一事务中多次读取相同数据会得到相同结果
- 解决了脏读和不可重复读
- InnoDB通过多版本并发控制(MVCC)实现
- InnoDB在此级别下通过间隙锁(Gap Lock)部分解决了幻读问题
可能问题:
- 在纯标准实现中可能存在幻读
- MySQL的InnoDB引擎在此级别下基本解决了幻读
示例:
-- 会话A
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
SELECT balance FROM accounts WHERE id = 1; -- 返回1000
-- 会话B
UPDATE accounts SET balance = 900 WHERE id = 1;
COMMIT;
-- 会话A再次查询
SELECT balance FROM accounts WHERE id = 1; -- 仍返回1000(可重复读)
4. SERIALIZABLE (可串行化)
特点:
- 最高隔离级别
- 通过完全锁定相关数据实现
- 性能最差,但一致性最好
- 解决了所有并发问题
示例:
-- 会话A
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
START TRANSACTION;
SELECT * FROM accounts WHERE id > 1; -- 会加共享锁
-- 会话B尝试修改被查询范围的数据
UPDATE accounts SET balance = balance + 100 WHERE id = 2; -- 会被阻塞
三、并发问题类型
问题类型 | 描述 | READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE |
---|---|---|---|---|---|
脏读 | 读取到其他事务未提交的数据 | 可能 | 不可能 | 不可能 | 不可能 |
不可重复读 | 同一事务内多次读取同一数据,结果不同(因其他事务修改并提交) | 可能 | 可能 | 不可能 | 不可能 |
幻读 | 同一事务内多次查询,得到不同行数的结果(因其他事务插入/删除并提交) | 可能 | 可能 | InnoDB不可能* | 不可能 |
*注:MySQL的InnoDB在REPEATABLE READ级别下通过间隙锁避免了大部分幻读情况
四、如何设置隔离级别
1. 全局设置
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
2. 会话级设置
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
3. 仅对下一个事务有效
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
START TRANSACTION;
-- ...
4. 查看当前隔离级别
SELECT @@GLOBAL.tx_isolation, @@SESSION.tx_isolation;
-- MySQL 8.0+:
SELECT @@GLOBAL.transaction_isolation, @@SESSION.transaction_isolation;
五、InnoDB的多版本并发控制(MVCC)
InnoDB通过MVCC实现非锁定读,主要机制:
- 每行记录有隐藏的创建版本号和删除版本号
- SELECT只查找版本早于当前事务版本的数据行
- INSERT/UPDATE/DELETE会创建新版本或标记删除版本
六、隔离级别选择建议
- READ UNCOMMITTED:几乎不使用,除非能接受脏读且追求最高性能
- READ COMMITTED:需要看到最新提交数据,能接受不可重复读
- REPEATABLE READ(默认):需要事务内一致性视图,大多数应用场景
- SERIALIZABLE:需要绝对一致性,能接受性能下降
七、实际应用示例
银行转账场景(需要REPEATABLE READ)
-- 会话A: 转账事务
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
-- 检查账户余额(1000元)
SELECT balance FROM accounts WHERE id = 1;
-- 转账200元
UPDATE accounts SET balance = balance - 200 WHERE id = 1;
UPDATE accounts SET balance = balance + 200 WHERE id = 2;
-- 此时其他会话看到的仍是原始余额
COMMIT;
报表生成场景(需要READ COMMITTED)
-- 需要看到最新的已提交数据
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
-- 生成基于当前已提交数据的报表
SELECT * FROM sales WHERE date = CURDATE();
COMMIT;
八、注意事项
- 隔离级别越高,并发性能通常越低
- InnoDB的REPEATABLE READ通过MVCC和间隙锁提供了比标准更严格的隔离
- 某些场景可能需要混合使用不同隔离级别
- 设置隔离级别不会影响已启动的事务
- 隔离级别不影响本事务看到自己的修改
通过合理选择事务隔离级别,可以在数据一致性和系统性能之间取得最佳平衡。大多数应用使用MySQL默认的REPEATABLE READ级别即可满足需求。