【mysql】事务隔离级别
事务隔离级别定义了并发事务间的数据可见性,在数据一致性和并发性能之间进行权衡。SQL标准定义了四种隔离级别:读未提交(可能脏读、不可重复读、幻读)、读已提交(避免脏读)、可重复读(避免脏读和不可重复读,MySQL默认级别)、串行化(避免所有问题)。MySQL的InnoDB引擎通过"临键锁"机制在可重复读级别下避免了幻读问题。隔离级别越高,一致性越强但性能越低,建议大多数应用使用
一. 什么是事务隔离级别?
事务隔离级别 定义了一个事务中的修改,在多大程度上对其他并发事务"可见"。它是在数据库的数据一致性和并发性能之间进行权衡的尺度。
为什么需要隔离级别?
如果所有事务都完全串行执行(一个接一个),虽然能保证绝对的一致性,但性能极差。为了支持并发,我们允许事务同时运行,但这会带来一些数据一致性问题。事务隔离级别就是用来控制这些问题的严格程度。
并发事务可能导致的三类典型问题:
-
脏读•定义:一个事务读到了另一个未提交事务修改的数据。•例子:事务A将账户余额从100元改为200元(但尚未提交)。此时事务B读取余额,读到了200元。随后事务A因故回滚,余额恢复为100元。那么事务B读到的200元就是无效的"脏数据"。
-
不可重复读•定义:在同一个事务内,多次读取同一行数据,结果不一致(因为其他已提交事务修改了该数据)。•例子:事务A第一次读取账户余额为100元。此时事务B提交了更新,将余额改为200元。事务A再次读取余额,发现变成了200元。同一个事务内的两次读取结果不一致。
-
幻读•定义:在同一个事务内,多次执行相同的查询,返回的结果集数量不一致(因为其他已提交事务插入或删除了数据)。•例子:事务A查询年龄小于30岁的用户有5人。此时事务B插入了一个新的25岁用户并提交。事务A再次查询,发现变成了6人。就像发生了"幻觉"一样,多出了一行。
注意:不可重复读和幻读的区别在于:
- 不可重复读 的重点是修改(UPDATE/DELETE)导致的数据值的变化。
- 幻读 的重点是新增或删除(INSERT/DELETE)导致的结果集行数的变化。
二.SQL标准定义的四种隔离级别
SQL标准定义了四种隔离级别,从低到高,限制越来越严格,能解决的问题也越多,但并发性能通常会随之降低。它们与上述问题的关系如下表所示:
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 数据库默认实现(常见) |
|---|---|---|---|---|
| 读未提交 (Read Uncommitted) | ❌ 可能 | ❌ 可能 | ❌ 可能 | 极少使用 |
| 读已提交 (Read Committed) | ✅ 避免 | ❌ 可能 | ❌ 可能 | Oracle, PostgreSQL, SQL Server |
| 可重复读 (Repeatable Read) | ✅ 避免 | ✅ 避免 | ❌ 可能 | MySQL (InnoDB) |
| 串行化 (Serializable) | ✅ 避免 | ✅ 避免 | ✅ 避免 | 极高一致性要求场景 |
各级别详解:
-
读未提交•限制最弱,性能最高,但一致性最差。•事务可以读取其他事务未提交的修改。几乎不会在生产环境使用。
-
读已提交•只能读取其他事务已经提交的修改。解决了脏读问题。•这是Oracle、PostgreSQL、SQL Server等数据库的默认隔离级别。
-
可重复读•确保在同一个事务中,多次读取同一数据的结果是一致的。解决了脏读和不可重复读问题。•这是MySQL的InnoDB存储引擎的默认隔离级别。
-
串行化•限制最强,性能最低,但一致性最高。•通过强制事务串行执行(而非并发)来避免所有问题。它会在读取的每一行上都加锁,可能导致大量的超时和锁等待。
三.MySQL的默认隔离级别是什么?
MySQL的默认隔离级别是:可重复读(REPEATABLE-READ)。
你可以通过以下命令查看MySQL的当前隔离级别和全局隔离级别:
-- 查看当前会话的隔离级别
SELECT @@transaction_isolation;
-- 查看全局的隔离级别
SELECT @@global.transaction_isolation;
在MySQL 5.x版本中,变量名是 tx_isolation。
为什么MySQL选择可重复读作为默认级别?
这是一个在一致性和性能之间的平衡选择。它提供了比"读已提交"更强的一致性保证(避免了不可重复读),同时又比"串行化"有更好的并发性能。
四.MySQL InnoDB对幻读的特殊处理
根据SQL标准,"可重复读"隔离级别是允许幻读发生的。但是,MySQL的InnoDB存储引擎通过"临键锁"机制,在可重复读隔离级别下就很大程度上避免了幻读。
这是MySQL对标准的一个增强,也是为什么很多人觉得MySQL的"可重复读"级别很好用的原因。
- 实现方式:InnoDB在可重复读级别下,使用Next-Key Lock(记录锁 + 间隙锁)来锁住查询涉及的范围。这不仅能锁住存在的记录,还能锁住记录之间的"间隙",防止其他事务在范围内插入新数据,从而避免了幻读。
示例:
-- 事务A
START TRANSACTION;
SELECT * FROM users WHERE age BETWEEN 20 AND 30 FOR UPDATE; -- 不仅锁住age在20-30的记录,还锁住了(20, 30)这个区间
-- 此时,事务B执行以下操作会被阻塞:
-- INSERT INTO users (name, age) VALUES ('John', 25); -- 需要等待事务A释放间隙锁
注意:FOR UPDATE(当前读)会加临键锁来避免幻读。而普通的快照读(不加锁的SELECT)利用MVCC(多版本并发控制)来提供一致性视图,本身就不会看到新插入的数据,所以也不会出现幻读。
五.总结
| 特性 | 说明 |
|---|---|
| 定义 | 控制事务间数据可见性的规则,权衡数据一致性和并发性能。 |
| 四种级别 | 读未提交 < 读已提交 < 可重复读 (MySQL默认) < 串行化 |
| 解决的问题 | 级别越高,能解决的并发问题越多(脏读、不可重复读、幻读)。 |
| MySQL的增强 | InnoDB在可重复读级别下通过临键锁避免了幻读。 |
如何选择隔离级别?
- 大多数应用:使用MySQL的默认级别可重复读即可,它在保证强一致性的同时提供了不错的并发性能。
- 对数据一致性要求极高,可以接受性能损失:考虑使用串行化。
- 追求最高并发性能,对脏读不敏感(极少见):可考虑读已提交。
我们清晰地看到:
- 隔离级别是权衡:一致性越强,并发性能越低
- MySQL默认选择可重复读:在保证强一致性的同时提供较好的性能
- InnoDB有特殊增强:通过临键锁机制,在可重复读级别就解决了幻读问题
- 问题递进关系:脏读 → 不可重复读 → 幻读,级别逐级解决
最佳实践:除非有充分理由,否则建议使用MySQL的默认隔离级别(可重复读)。
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)