Skip to content

Mysql事务是如何实现的

1. 原子性 (Atomicity) 的实现:Undo Log

原子性要求一个事务中的所有操作,要么全部成功执行,要么全部失败回滚。InnoDB 使用 Undo Log(撤销日志)来保证这一点。

  • 工作原理:当一个事务需要对数据进行修改时,InnoDB 会首先将数据的旧版本(修改前的数据)记录到 Undo Log 中。这个日志记录的是逻辑上的“反向操作”。
  • 实现回滚:如果事务因为任何原因需要回滚(比如用户执行 ROLLBACK,或者发生错误),InnoDB 就会找到该事务对应的 Undo Log,并按照相反的顺序执行其中的操作,从而将数据恢复到事务开始前的初始状态。
  • 实现原子性:即使在事务执行过程中数据库发生崩溃,重启后 InnoDB 依然可以通过 Undo Log 来回滚那些已经执行但尚未提交的事务,确保了“要么全做,要么全不做”的原子性。

2. 隔离性 (Isolation) 的实现:MVCC 与锁

隔离性要求并发执行的事务之间互不干扰,看起来就像是串行执行一样。InnoDB 主要通过 MVCC(多版本并发控制)和锁机制来实现隔离性。

  1. MVCC (Multi-Version Concurrency Control):

    这是实现读-写并发的关键,主要用于处理“快照读”(普通的 SELECT)。它通过我们之前讨论过的 Undo Log 版本链和 ReadView(读视图)机制,为每个读事务提供一个一致性的数据快照。这样,读操作就不需要等待写操作释放锁,而是可以直接读取数据的历史版本,极大地提升了并发性能。不同隔离级别(如读已提交、可重复读)的差异,主要就体现在 ReadView 的创建时机上。

  2. 锁 (Locking):

    当 MVCC 无法解决冲突时(主要是写-写冲突),InnoDB 就会使用锁机制。这主要用于处理“当前读”(如 SELECT...FOR UPDATE, UPDATE, DELETE)。当一个事务需要修改数据时,它会先获取该数据的排他锁(X 锁),阻止其他事务对该数据进行任何修改,直到当前事务提交或回滚释放该锁。通过锁,InnoDB 保证了在任何时刻,最多只有一个事务能修改同一份数据。

3. 持久性 (Durability) 的实现:Redo Log 与 WAL

持久性要求一旦事务提交,其对数据库的修改就是永久性的,即使发生系统崩溃也不会丢失。InnoDB 使用 Redo Log(重做日志)和 WAL(Write-Ahead Logging,预写日志)技术来高效地实现持久性。

  • WAL 技术:当数据需要修改时,InnoDB 会先修改内存中的数据页(在 Buffer Pool 里),然后将这次修改的物理操作记录到 Redo Log 中。事务提交时,并不需要立即将修改过的数据页刷回磁盘(这是一个慢速的随机 IO),而只需要保证对应的 Redo Log 已经刷入磁盘(这是一个快速的顺序 IO)。
  • 实现持久性:只要 Redo Log 成功写入磁盘,事务的提交就被认为是完成了。如果此时数据库宕机,内存中的数据修改虽然丢失了,但重启后 InnoDB 可以通过重放磁盘上的 Redo Log,将所有已提交事务的修改重新应用到数据页上,从而恢复到宕机前的状态,保证了数据的持久性。

4. 一致性 (Consistency) 的实现:多方协同的结果

一致性是事务追求的最终目标,它要求数据库从一个一致性状态转变到另一个一致性状态。它不是由单一技术实现的,而是由原子性、隔离性和持久性共同保障的结果。

  • 原子性保证了事务的完整执行或完全不执行,防止了因部分操作失败而导致的数据不一致。
  • 隔离性保证了并发事务之间不会产生异常的中间状态,使得每个事务都感觉自己是在一个独立的环境中运行。
  • 持久性保证了已提交的事务结果不会因系统故障而丢失,维护了数据状态的稳定。

此外,数据库本身定义的约束(如主键、外键、唯一性约束等)也是保证一致性的重要部分。

5. 一个事务的完整生命周期

我们可以通过一个 UPDATE 事务的生命周期来串联起这些机制:

  1. 事务开始。
  2. 执行 UPDATE 语句:
    • InnoDB 首先需要执行一个“当前读”,获取这行数据的排他锁。
    • 将这行数据的原始值写入 Undo Log。
    • 在内存的 Buffer Pool 中修改这行数据。
    • 将这个修改操作的物理变更记录到 Redo Log Buffer 中。
  3. 执行 COMMIT
    • 这是一个“两阶段提交”过程,为了协调 Redo Log 和 Binlog 的一致性。
    • 第一阶段:Redo Log 进入 prepare 状态,并刷入磁盘。
    • 第二阶段:如果开启了 Binlog,则将 Binlog 写入磁盘。
    • 第三阶段:Redo Log 进入 commit 状态。
    • 一旦 Redo Log commit 完成,事务就被认为是成功提交并持久化了。
  4. 后台线程会在后续某个合适的时机,将 Buffer Pool 中被修改过的数据页(脏页)异步地刷回到磁盘文件中。