一. 什么是事务隔离级别?

事务隔离级别 定义了一个事务中的修改,在多大程度上对其他并发事务"可见"。它是在数据库的数据一致性并发性能之间进行权衡的尺度。

为什么需要隔离级别?

如果所有事务都完全串行执行(一个接一个),虽然能保证绝对的一致性,但性能极差。为了支持并发,我们允许事务同时运行,但这会带来一些数据一致性问题。事务隔离级别就是用来控制这些问题的严格程度。

并发事务可能导致的三类典型问题:

  1. 脏读定义:一个事务读到了另一个未提交事务修改的数据。•例子:事务A将账户余额从100元改为200元(但尚未提交)。此时事务B读取余额,读到了200元。随后事务A因故回滚,余额恢复为100元。那么事务B读到的200元就是无效的"脏数据"。

    事务A 数据库 事务B 读未提交级别下发生脏读 UPDATE SET balance=200 (未提交) SELECT balance → 读到200 读到了事务A未提交的数据 ROLLBACK (回滚到100) 事务B读到的200是"脏数据" 事务A 数据库 事务B
  2. 不可重复读定义:在同一个事务内,多次读取同一行数据,结果不一致(因为其他已提交事务修改了该数据)。•例子:事务A第一次读取账户余额为100元。此时事务B提交了更新,将余额改为200元。事务A再次读取余额,发现变成了200元。同一个事务内的两次读取结果不一致。

    事务A 数据库 事务B 读已提交级别下发生不可重复读 SELECT balance → 返回100 UPDATE SET balance=200 (提交) SELECT balance → 返回200 同一事务内两次读取结果不一致! 事务A 数据库 事务B
  3. 幻读定义:在同一个事务内,多次执行相同的查询,返回的结果集数量不一致(因为其他已提交事务插入删除了数据)。•例子:事务A查询年龄小于30岁的用户有5人。此时事务B插入了一个新的25岁用户并提交。事务A再次查询,发现变成了6人。就像发生了"幻觉"一样,多出了一行。

    事务A 数据库 事务B 可重复读级别下可能发生幻读 SELECT COUNT(*) FROM users → 返回5 INSERT INTO users ... (提交) SELECT COUNT(*) FROM users → 返回6 同一事务内两次查询结果集行数不同! 事务A 数据库 事务B

注意:不可重复读和幻读的区别在于:

  • 不可重复读 的重点是修改(UPDATE/DELETE)导致的数据值的变化
  • 幻读 的重点是新增或删除(INSERT/DELETE)导致的结果集行数的变化

二.SQL标准定义的四种隔离级别

SQL标准定义了四种隔离级别,从低到高,限制越来越严格,能解决的问题也越多,但并发性能通常会随之降低。它们与上述问题的关系如下表所示:

隔离级别 脏读 不可重复读 幻读 数据库默认实现(常见)
读未提交 (Read Uncommitted) ❌ 可能 ❌ 可能 ❌ 可能 极少使用
读已提交 (Read Committed) ✅ 避免 ❌ 可能 ❌ 可能 Oracle, PostgreSQL, SQL Server
可重复读 (Repeatable Read) ✅ 避免 ✅ 避免 ❌ 可能 MySQL (InnoDB)
串行化 (Serializable) ✅ 避免 ✅ 避免 ✅ 避免 极高一致性要求场景

各级别详解:

  1. 读未提交•限制最弱,性能最高,但一致性最差。•事务可以读取其他事务未提交的修改。几乎不会在生产环境使用。

  2. 读已提交•只能读取其他事务已经提交的修改。解决了脏读问题。•这是Oracle、PostgreSQL、SQL Server等数据库的默认隔离级别

  3. 可重复读•确保在同一个事务中,多次读取同一数据的结果是一致的。解决了脏读和不可重复读问题。•这是MySQL的InnoDB存储引擎的默认隔离级别

  4. 串行化•限制最强,性能最低,但一致性最高。•通过强制事务串行执行(而非并发)来避免所有问题。它会在读取的每一行上都加锁,可能导致大量的超时和锁等待。

    并发事务问题
    选择隔离级别
    读未提交
    读已提交
    可重复读
    串行化
    X 脏读
    X 不可重复读
    X 幻读
    V 避免脏读
    X 不可重复读
    X 幻读
    V 避免脏读
    V 避免不可重复读
    X 可能幻读
    V 避免脏读
    V 避免不可重复读
    V 避免幻读
    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的默认级别可重复读即可,它在保证强一致性的同时提供了不错的并发性能。
  • 对数据一致性要求极高,可以接受性能损失:考虑使用串行化
  • 追求最高并发性能,对脏读不敏感(极少见):可考虑读已提交

我们清晰地看到:

  1. 隔离级别是权衡:一致性越强,并发性能越低
  2. MySQL默认选择可重复读:在保证强一致性的同时提供较好的性能
  3. InnoDB有特殊增强:通过临键锁机制,在可重复读级别就解决了幻读问题
  4. 问题递进关系:脏读 → 不可重复读 → 幻读,级别逐级解决

最佳实践:除非有充分理由,否则建议使用MySQL的默认隔离级别(可重复读)

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐