暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MySQL事务隔离级别

卡布奇诺海晨 2025-06-05
153

MySQL 的事务隔离级别定义了事务在并发执行时,彼此之间的可见性以及可能遇到的并发问题(脏读、不可重复读、幻读)。SQL 标准定义了四个隔离级别,MySQL 的 InnoDB 存储引擎支持所有这些级别,并在默认的 REPEATABLE READ
 级别下提供了额外的保证。

以下是 MySQL 四个事务隔离级别的详细说明、可能引发的问题以及 InnoDB 的具体实现行为:

隔离级别
脏读 (Dirty Read)
不可重复读 (Non-Repeatable Read)
幻读 (Phantom Read)
InnoDB 实现特点
READ UNCOMMITTED
 (读未提交)
可能可能可能
直接读取数据页最新值,不使用 MVCC 快照,性能最好但一致性最差。几乎不用。
READ COMMITTED
 (读已提交)
不可能可能可能
每次 SELECT
 都生成新 Read View (快照),看到的是最新已提交的数据。使用 MVCC 和 记录锁 (无间隙锁)。
REPEATABLE READ
 (可重复读)
不可能不可能可能(理论)默认级别
。事务内第一次 SELECT
 时生成 Read View
,后续复用。使用 MVCC 和 Next-Key Locking (记录锁+间隙锁)。InnoDB 通过 Next-Key Lock 在 当前读
 场景下避免了幻读
SERIALIZABLE
 (串行化)
不可能不可能不可能
所有 SELECT
 自动转为 SELECT ... LOCK IN SHARE MODE
加共享锁,读写严重互斥,并发性最低。不使用 MVCC 快照读。

关键概念解释:

  1. 脏读 (Dirty Read):

    • 一个事务读到了另一个未提交事务修改的数据。

    • 危害: 读取到可能被回滚的无效数据,导致业务逻辑错误。

    • 解决: READ COMMITTED
       及以上级别避免。

  2. 不可重复读 (Non-Repeatable Read):

    • 在同一个事务内,两次读取同一条记录,得到了不同的结果(因为其他事务在中间修改并提交了该记录)。

    • 危害: 破坏了事务内数据一致性预期。例如,事务内基于第一次读的值做计算,第二次读时值变了。

    • 解决: REPEATABLE READ
       及以上级别避免 (通过 MVCC 的快照或锁)。

  3. 幻读 (Phantom Read):

    • 在同一个事务内,两次执行相同的查询,得到了不同的结果集(因为其他事务在中间插入或删除了符合查询条件的记录并提交)。

    • 危害: 范围查询的结果集不稳定,影响统计、分页等操作。

    • 注意点: SQL 标准中 REPEATABLE READ
       允许幻读。但 InnoDB 在 REPEATABLE READ
       级别下通过 Next-Key Lock
       机制,在 当前读
       (SELECT ... FOR UPDATE
      UPDATE
      DELETE
      ) 场景下避免了幻读
      。对于快照读
       (SELECT
      ),因为读取的是事务开始时的快照,理论上不会看到新插入的数据(即不会“幻象”),但如果后续在同一个事务内执行了会修改数据的操作(当前读),再执行快照读,可能会看到新插入的数据(MVCC 可见性规则决定的),这种现象有时也被称为幻读的一种表现,但严格来说与 SQL 标准定义的幻读(两次查询结果集不同)略有区别。SERIALIZABLE
       则完全避免。

InnoDB 各隔离级别的具体实现细节:

  1. READ UNCOMMITTED:

    • 行为: 事务可以看到其他事务尚未提交的修改。直接读取数据页上的最新值。

    • 问题: 脏读、不可重复读、幻读都可能发生。

    • 使用场景: 极少使用,除非对数据一致性要求极低且追求极致性能(但通常得不偿失)。

    • 设置: SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
       或 SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

  2. READ COMMITTED (RC):

  • 行为:

    • 使用 MVCC:每次执行普通的 SELECT
       语句(快照读)时,都会生成一个新的 Read View。因此,它总是能看到查询开始前最新已提交的数据。

    • 对于 SELECT ... FOR UPDATE
      UPDATE
      DELETE
       (当前读):只会锁定涉及到的具体行记录(记录锁)不会加间隙锁 (Gap Locks) 或 Next-Key Locks。

  • 优点:

    • 避免了脏读。

    • 锁的粒度相对较小(无间隙锁),减少锁冲突,提高并发性能(尤其在写冲突不高的场景)。

    • 可以看到其他事务已提交的新数据。

  • 问题:

    • 不可重复读: 事务内两次读同一行,如果中间有其他事务提交了修改,结果可能不同。

    • 幻读 (可能): 因为不加间隙锁,其他事务可以插入新的记录到查询范围内并提交,导致后续查询看到新行。

  • 使用场景: 需要避免脏读,可以接受不可重复读和可能的幻读。常用于报表查询、数据导出、对一致性要求不是极高但并发要求较高的 OLTP 场景Oracle 默认级别

  • 设置: SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
     或 SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;


3. REPEATABLE READ (RR):

  • 行为 (默认级别):

    • 使用 MVCC:在事务中第一次执行快照读 (SELECT
      )
       时生成一个 Read View。整个事务期间,后续所有的快照读都复用这个相同的 Read View。因此,事务内看到的是一致的历史快照。

    • 对于 SELECT ... FOR UPDATE
      UPDATE
      DELETE
       (当前读):使用 Next-Key Locking 算法(记录锁 + 间隙锁)。这不仅能锁定查询命中的记录,还能锁定记录之间的间隙,阻止其他事务在查询范围内插入新的记录

  • 优点:

    • 避免了脏读。

    • 避免了不可重复读 (MVCC 快照)。

    • 在当前读操作 (FOR UPDATE
      UPDATE
      DELETE
      ) 下避免了幻读 (Next-Key Lock)
      。这是 InnoDB 对 SQL 标准的扩展增强。

    • 对于纯快照读 (SELECT
      ),因为始终读取事务开始时的快照,理论上不会看到新插入的数据(“幻象行”)。

  • 问题:

    • 使用 Next-Key Lock 会增加锁的范围和锁冲突的概率,可能降低并发写入性能。

    • 长事务可能导致大量旧版本数据无法被 Purge,占用 Undo Log 空间。

  • 使用场景: MySQL InnoDB 的默认级别。适用于需要高度数据一致性、对不可重复读和幻读有要求的场景,如金融交易、账户管理等。也是大多数应用推荐的级别。

  • 设置: (默认级别,通常无需显式设置) SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
     或 SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;


4. SERIALIZABLE:

  • 行为:

    • 不使用 MVCC 的快照读。所有普通的 SELECT
       语句自动隐式转换为 SELECT ... LOCK IN SHARE MODE

    • 这意味着读取会加共享锁 (S Lock)

    • 写操作(UPDATE
      DELETE
      INSERT
      SELECT ... FOR UPDATE
      )仍然需要获取排他锁 (X Lock)。

  • 优点: 完全避免了脏读、不可重复读、幻读。提供了最强的隔离性。

  • 缺点:

    • 并发性最低。读写严重互斥(S 锁阻塞 X 锁请求,X 锁阻塞所有锁请求),容易导致大量锁等待和性能下降。

    • 死锁风险增加。

  • 使用场景: 对数据一致性要求极高,且可以接受极低并发性能的场景。实际应用中非常少见

  • 设置: SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
     或 SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;


如何设置和查看隔离级别?

  • 查看全局/会话隔离级别:

    SELECT @@GLOBAL.transaction_isolation, @@SESSION.transaction_isolation;
    -- 或 (旧变量名,8.0+ 推荐用上面的)
    SELECT @@GLOBAL.tx_isolation, @@SESSION.tx_isolation;


    • 设置全局隔离级别 (重启后生效,影响后续所有新会话):

      SETGLOBAL transaction_isolation ='READ-COMMITTED';
      • 设置当前会话隔离级别 (仅影响当前连接):

        SET SESSION transaction_isolation = 'REPEATABLE-READ'; -- 或 'READ-COMMITTED'
        • 设置下一个事务的隔离级别:

          SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
          START TRANSACTION; -- 下一个开始的事务将使用此级别


          选择建议:

          1. 优先使用 REPEATABLE READ
             (默认):
             它在保证良好一致性的同时提供了不错的并发性能。InnoDB 通过 MVCC 和 Next-Key Lock 巧妙地解决了大部分并发问题。

          2. 考虑 READ COMMITTED
             的场景:

            • 对不可重复读和幻读不敏感的应用。

            • 需要看到最新已提交数据的报表查询或只读从库。

            • 写冲突较少,且希望减少锁争用、提高并发写入吞吐量。

            • 使用基于语句的复制 (SBR) 时,主从切换可能更安全(但推荐使用基于行的复制 RBR)。

          3. 避免使用 READ UNCOMMITTED
             和 SERIALIZABLE
             前者牺牲一致性,后者牺牲性能,除非有非常特殊的需求。

          重要提示:

          • 隔离级别的选择需要在数据一致性并发性能之间做权衡。没有绝对的最佳,只有最适合当前业务场景的。

          • 理解不同级别下 MVCC 和 锁机制(尤其是 Next-Key Lock 在 RR 下的作用)的行为是掌握隔离级别的关键。

          • 在 REPEATABLE READ
             下,长事务可能导致 Undo Log 膨胀和锁持有时间过长,需注意管理事务边界。

          • 即使使用了较高的隔离级别,良好的数据库设计(合理索引、避免全表扫描)和事务设计(短事务、按相同顺序访问资源)对避免死锁和性能问题至关重要。


          文章转载自卡布奇诺海晨,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

          评论