mysql事务是怎么实现的

注意:这里忽略了binlog
binlog 的作用:

  • 主从复制:在主从复制架构中,主库的 binlog 记录了所有引起数据变更的事务操作。从库通过读取主库的 binlog 并在自己的数据库上重放这些事务,实现与主库的数据同步。当主库宕机恢复后,从库可以继续从主库获取新的 binlog 事件,保持数据一致。
  • 崩溃恢复和数据一致性:在 MySQL 的某些存储引擎(如 InnoDB)中,为了保证 binlog 和 redo log 的一致性,引入了两阶段提交(Two-Phase Commit,2PC)协议。在事务提交过程中,先将 redo log 写入并处于 prepare 状态,然后将 binlog 写入,最后将 redo log 状态改为 commit。如果在这个过程中数据库宕机,重启时会检查 binlog 和 redo log 的状态,根据两阶段提交协议来决定事务是否需要提交或回滚,以保证数据的一致性。

在MySQL中,事务的实现主要依赖于InnoDB引擎(唯一支持完整事务特性的存储引擎),通过日志系统(undo/redo日志)锁机制MVCC(多版本并发控制)等核心技术,实现ACID特性。以下是详细实现原理:

一、ACID特性的实现基础

1. 原子性(Atomicity):undo日志回滚

  • undo日志:记录事务对数据的反向操作(如删除前的旧值、更新前的旧值),用于回滚事务。
    • 每个事务开始时,InnoDB为其分配一个事务ID(DB_TRX_ID),并在修改数据前将旧值写入undo日志。
    • 若事务回滚,InnoDB根据undo日志恢复数据到事务开始前的状态。
  • 示例
    UPDATE users SET balance = balance - 100 WHERE id = 1;  -- 扣款操作
    
    undo日志记录旧的balance值(如500),若事务回滚,直接将balance恢复为500。

2. 持久性(Durability):redo日志持久化

  • redo日志(重做日志):记录事务对数据的正向修改,用于故障恢复(即使数据库崩溃,重启后可通过redo日志重建未提交的数据)。
  • WAL(Write-Ahead Logging)机制:
    • 事务提交时,先将redo日志写入磁盘(通过fsync确保落盘),再更新数据页(可能延迟写入磁盘)。
    • redo日志包含LSN(日志序列号),用于标记日志的写入位置,确保恢复时按顺序应用。

3. 隔离性(Isolation):锁与MVCC

  • 锁机制
    • 写锁(X锁):独占数据修改,阻止其他事务读写(解决写-写、写-读冲突)。
    • 读锁(S锁):共享读,阻止写锁(解决读-写冲突)。
    • 间隙锁(Gap Lock):在可重复读隔离级别下,阻止幻读(锁定索引间隙,防止插入新数据)。
  • MVCC(多版本并发控制)
    • 为每行数据维护多个版本(通过隐藏字段DB_TRX_IDDB_ROLL_PTR),快照读(普通SELECT)通过Read View访问历史版本,避免锁竞争。
    • Read View包含当前活跃事务ID列表,判断数据版本是否可见(未提交的版本对当前事务不可见)。

4. 一致性(Consistency):ACID协同保障

  • 一致性是最终目标,依赖原子性(确保单事务正确)、隔离性(确保事务间不干扰)、持久性(确保修改持久化)共同实现,同时结合数据库约束(如唯一索引、外键)。

二、事务的生命周期与关键步骤

1. 事务启动

  • 通过START TRANSACTIONBEGIN显式启动,或隐式启动(如DML语句自动开启事务)。
  • InnoDB分配唯一的事务ID(全局递增,保证顺序性)。

2. 事务执行(DML操作)

UPDATE操作为例:

  1. 生成操作日志
    • 记录undo日志(旧值)和redo日志(新值)。
    • 示例:更新balance从500到400,undo日志记录(id=1, balance=500),redo日志记录(id=1, balance=400)
  2. 修改内存数据
    • 在内存中的数据页(Buffer Pool)中更新balance为400,标记为“脏页”。
  3. 锁处理
    • 对更新的行加X锁(排他锁),阻止其他事务修改。

3. 事务提交(COMMIT)

  1. 写入redo日志
    • 将redo日志从缓存写入磁盘(fsync确保持久化),事务状态标记为“已提交”。
  2. 释放锁
    • 释放事务持有的所有锁(X锁/S锁),允许其他事务访问数据。
  3. 异步刷脏页
    • 内存中的脏页由后台线程逐步刷入磁盘(不阻塞提交,提升性能)。

4. 事务回滚(ROLLBACK)

  1. 读取undo日志
    • 根据undo日志中的旧值,恢复数据页到事务开始前的状态(如balance从400恢复为500)。
  2. 释放锁
    • 释放所有锁,事务结束。

三、关键数据结构与日志格式

1. undo日志的两种类型

  • INSERT Undo Log:记录插入操作的反向操作(仅在事务回滚时使用,提交后可删除)。
  • UPDATE Undo Log:记录更新/删除操作的旧值(用于MVCC,保留一定时间供其他事务的快照读使用)。

2. redo日志的物理格式

  • 包含操作类型(如更新、插入)、数据页地址修改前后的值等,用于快速重做操作。
  • 示例:
    [LSN=1000] UPDATE RECORD (page=10, offset=50, old_val=500, new_val=400)
    

3. MVCC的隐藏字段

每行数据包含:

  • DB_TRX_ID:最后一次修改数据的事务ID。
  • DB_ROLL_PTR:指向undo日志中该版本的前一个版本(形成版本链)。
  • DB_ROW_ID:隐藏自增主键(若无显式主键)。

四、隔离级别与实现差异

隔离级别 核心实现机制 锁与MVCC配合
读未提交 直接读取最新数据(不使用MVCC) 写操作加X锁,读操作无锁(允许脏读)
读已提交 每次快照读生成新的Read View(MVCC) 写操作加X锁,读操作无锁(通过版本控制避免脏读)
可重复读 事务启动时生成Read View(MVCC) 写操作加X锁,范围查询加间隙锁(防幻读)
串行化 完全依赖锁(S/X锁),禁用MVCC 读加S锁,写加X锁,读写互斥(串行执行)

五、故障恢复机制

  1. 崩溃恢复(Crash Recovery)
    • 数据库重启时,InnoDB通过redo日志重做未提交的事务(已写入redo日志但未刷脏页的操作)。
    • 通过undo日志回滚未提交的事务(事务ID未在系统表中标记为提交)。
  2. ACID的最后保障
    • 即使发生断电等故障,redo日志确保已提交事务的数据持久化,undo日志确保未提交事务回滚。

六、总结:事务实现的核心逻辑

  1. 原子性:undo日志记录反向操作,回滚时恢复数据。
  2. 持久性:redo日志通过WAL机制先于数据落盘,确保不丢失。
  3. 隔离性:锁机制解决写冲突,MVCC实现无锁快照读,共同满足隔离级别。
  4. 一致性:通过上述机制及数据库约束,确保事务前后数据合法。

一句话核心:

MySQL事务通过undo日志实现回滚(原子性)、redo日志实现持久化(持久性)、锁与MVCC实现隔离性,三者协同工作,结合数据库约束,最终保障数据一致性。

posted on 2025-04-17 22:33  斜月三星一太阳  阅读(130)  评论(0)    收藏  举报