MySQL事务机制

深入浅出MySQL事务

Posted by Zenkai on August 10, 2025

MySQL事务机制

事务的特性

事务必须要遵守 4 个特性,分别如下:

  • 原子性(Automicity):一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节,而且事务执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从未被执行过一样;
  • 一致性(Consistency):是指事务操作前和操作后,数据满足完整性约束,数据库保持一致性状态;
  • 隔离性(Isolation):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致;
  • 持久性(Durability):事务处理结束后,对数据的修改是永久的;

InnoDB引擎通过什么技术来保证事务的ACID特性

  • 持久性:通过redo log(重做日志)
  • 原子性:通过undo log(回滚日志)
  • 隔离性:通过MVCC(多版本并发控制)或者锁机制
  • 一致性:通过持久性+原子性+隔离性

Redo Log与Undo Log介绍

1. Redo Log(重做日志)

redo log 主要用于保证数据库的持久性(Durability)和崩溃恢复。它记录了事务对数据库所做的修改,确保这些修改能够在崩溃后重新应用

作用:

  • 确保事务的持久性
    • 当一个事务执行修改操作时,InnoDB 会先将这些修改写入到 redo log 中,随后才会将数据更新写入到磁盘数据文件(即 ibdata 文件)中。这样可以确保即使在事务提交后系统崩溃,已经提交的事务的修改依然能够恢复。
  • 崩溃恢复时的事务重做
    • 在崩溃恢复过程中,InnoDB 会通过 redo log 文件来重新应用已经提交的事务。这是因为 redo log 是事务操作的持久化记录,可以确保崩溃后,已提交的事务不会丢失。
    • 恢复过程会扫描 redo log 中的所有记录,并将这些操作应用到数据页中。

具体过程:

  1. 写入日志:事务开始时,所有数据修改都会首先记录到 redo log 中(在 log buffer 中)。
  2. 提交事务时刷新日志:当事务提交时,InnoDB 会确保 redo log 被刷新到磁盘,标记事务提交。
  3. 崩溃恢复时应用日志:崩溃恢复时,MySQL 会读取 redo log 中的日志条目,并重新执行已提交事务的操作,从而恢复数据。

2. Undo Log(撤销日志)

undo log 用于保证数据库的原子性(Atomicity)和一致性(Consistency),以及提供事务回滚的能力。它记录了事务的操作状态,使得事务可以在出错时撤销已执行的操作,恢复到事务开始之前的状态。

作用:

  • 事务回滚
    • 如果事务未提交(比如崩溃前),InnoDB 会使用 undo log 来撤销事务的更改。它通过记录的前值,将数据恢复到事务开始之前的状态。
  • 保证原子性和一致性
    • 在事务执行过程中,如果发生崩溃,InnoDB 需要确保所有未提交的事务操作能够被撤销,以保持数据库的原子性(即事务要么完全成功,要么完全回滚)。
    • undo log 使得回滚操作可以有效地恢复到一致性状态,确保数据库数据没有部分更新。

具体过程:

  1. 记录前镜像:事务执行时,InnoDB 会将每个修改操作的前值记录在 undo log 中。也就是说,undo log 记录的是操作之前的状态。
  2. 事务回滚时使用:如果事务被回滚,或者数据库崩溃时存在未提交的事务,InnoDB 会根据 undo log 中的记录,将数据恢复到事务开始之前的状态。
  3. 事务撤销:在崩溃恢复时,InnoDB 会通过 undo log 撤销所有未提交的事务。这些操作不会影响已提交的事务,只会影响那些没有成功提交的事务。

Redo Log 和 Undo Log 的协作

  • Redo log 负责将已提交的事务的修改持久化到磁盘,确保即使崩溃发生,已提交的事务依然能够恢复。
  • Undo log 负责确保未提交事务的修改不会对数据库产生影响,它撤销未提交事务的修改,恢复数据库的原始状态。

崩溃恢复过程中的步骤:

  1. 恢复 redo log
    • 恢复过程中,InnoDB 会首先扫描 redo log,并将所有已提交的事务的修改应用到数据文件中。即使在事务提交后崩溃,只要 redo log 中有对应的记录,恢复时就能重新应用这些修改。
  2. 撤销未提交事务
    • 对于在崩溃时未提交的事务,InnoDB 会扫描 undo log 来撤销这些未提交的事务所做的修改,确保数据库的一致性。
  3. 最终一致性检查
    • 恢复过程结束后,InnoDB 会进行一致性检查,确保所有的已提交事务在数据文件中都有持久化的记录,并且未提交的事务已经完全回滚。

总结:

  • Redo Log:保证事务提交后的修改能够在崩溃后恢复,确保事务的持久性。
  • Undo Log:保证未提交事务的修改在崩溃恢复后被撤销,确保事务的原子性和一致性。

这两者配合,能够保证 MySQL 在事务崩溃后的恢复过程中维持数据库的一致性和完整性。

事务的隔离级别有哪些?

当多个事务并发执行,可能会遇到「脏读、不可重复读、幻读」,这些现象会对数据一致性产生影响;

SQL 标准提出了四种隔离级别来规避这些现象,隔离级别越高,性能效率就越低,这四个隔离级别如下:

  • 读未提交(*read uncommitted*),指一个事务还没提交时,它做的变更就能被其他事务看到;
  • 读提交(*read committed*),指一个事务提交之后,它做的变更才能被其他事务看到;
  • 可重复读(*repeatable read*),指一个事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,MySQL InnoDB 引擎的默认隔离级别
  • 串行化(*serializable* ),会对记录加上读写锁,在多个事务对这条记录进行读写操作时,如果发生了读写冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行;

MySQL InnoDB 引擎的默认隔离级别虽然是「可重复读」,但是它很大程度上避免幻读现象

  • 针对快照读(普通 select 语句),是通过 MVCC 方式解决了幻读,因为可重复读隔离级别下,事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,即使中途有其他事务插入了一条数据,是查询不出来这条数据的,所以就很好了避免幻读问题。
  • 针对当前读(select … for update 等语句),是通过 next-key lock(记录锁+间隙锁)方式解决了幻读,因为当执行 select … for update 语句的时候,会加上 next-key lock,如果有其他事务在 next-key lock 锁范围内插入了一条记录,那么这个插入语句就会被阻塞,无法成功插入,所以就很好了避免幻读问题。