WAL(Write-Ahead Logging)
WAL 是 MySQL 数据库的核心日志机制,其核心原则是在将数据变更应用到数据库之前,先将这些变更写入日志。这种方式保证了即使系统发生崩溃,数据库也能通过回放日志恢复到一致的状态。
以 InnoDB 存储引擎为例,当事务修改数据时,MySQL 会先将变更记录到 Redo log 中,再将数据写入磁盘。这样可以避免每次数据修改都要直接写入到数据文件,因为日志文件的写入操作通常比直接写入数据文件更高效。这种机制减少了直接磁盘 IO 的开销,并确保在崩溃恢复时能通过日志重放恢复未落盘的数据。
MySQL 三大日志
MySQL 使用多种日志来实现不同的功能,不同的日志类型解决了数据库运行中不同层面的关键问题,最常见的是 Redo log、Undo log 和 Binlog。它们都遵循 WAL 机制,但用途和实现方式不同。
- Redo log 是 InnoDB 存储引擎级别的日志,主要服务于崩溃恢复。
- Undo log 是 InnoDB 存储引擎级别的日志,主要用于事务回滚和多版本并发控制(MVCC)的实现。
- Binlog 是 MySQL 服务级别的日志,主要服务于数据备份、主从复制和数据恢复。
Redo Log
Redo Log 是 MySQL InnoDB 存储引擎的重要组成部分,用于记录数据页的物理变更,保证事务的持久性和数据恢复,确保了数据库的稳定性和数据的一致性。
Redo Log 首先存储在内存中的日志缓冲区中,在合适的时间从缓冲区刷写到磁盘上的日志文件中,默认有 ib_logfile1 和 ib_logfile2 两个日志文件,大小固定,循环使用。Redo Log 中记录的数据页变更信息通常包括表空间 ID、页号、偏移量和新值等。有了 Redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为 crash-safe。
- 作用:用于崩溃恢复(Crash recovery)。
- 特点:Redo log 是物理日志,记录的是数据库 Page 的低级别变更。当事务提交时,Redo log 确保这些变更可以在系统崩溃后被重新应用到数据库中,从而保证已提交事务的持久性。
Undo Log
Undo Log 是 MySQL InnoDB 存储引擎中的一种核心日志机制,它主要解决了事务的回滚和实现多版本并发控制 (MVCC)。它是保证数据库 ACID 特性 中 原子性和隔离性的重要基础。
- 事务回滚
- 当一个事务需要回滚时,Undo Log 记录了该事务修改数据之前的旧版本数据,引擎利用这些记录逆向执行操作,将数据恢复到事务开始前的状态。
- 实现多版本并发控制(MVCC)
- 当其他事务需要读取某行数据时,如果该行数据被另一个未提交的事务修改了,或者需要读取一个『一致性快照』时,InnoDB 可以通过 Undo Log 链找到该行数据的合适的历史版本提供给读取者。这使得『非锁定读』成为可能,大大提高了并发读性能。
Binlog
- 作用:用于复制(Replication)和时间点恢复(point-in-time recovery)。
- 特点:Binlog 是逻辑日志,记录的是行级别的变更(包括数据和架构的修改)。它可以让从服务器(slave)同步主服务器(master)的变更,或帮助数据库恢复到特定时间点。
两阶段提交
MySQL 的两阶段提交是 WAL 技术的具体实现 ,用于协调 Redo Log(InnoDB 层) 和 Binlog(Server 层) 的一致性。由于事务可能同时涉及存储引擎层(Redo Log)和 Server 层(Binlog),两阶段提交确保两者在事务提交时保持原子性和一致性。其核心流程如下:
Prepare 阶段
- InnoDB 将事务的 Redo Log 写入日志文件,并标记为 PREPARE 状态。
- MySQL Server 将事务的 Binlog 写入日志文件,但暂不提交。
Commit 阶段
- InnoDB 确认 Redo Log 已持久化后,Server 层提交事务,Binlog 标记为提交状态。
- 最终 Redo Log 更新为 COMMIT 状态。
通过这种分阶段提交,即使在提交过程中发生崩溃,MySQL 也能通过 Redo Log 和 Binlog 的协调恢复数据。
相关问题
为何需要两阶段提交?
如果只有 Redo log 或者只有 Binlog,那么事务就不需要两阶段提交。但是如果同时使用了 Redo log 和 Binlog,那么就需要保证这两种日志之间的一致性。
- 数据一致性:Redo Log 和 Binlog 是 MySQL 实现崩溃恢复(Crash Recovery)和主从复制(Replication)的关键日志。若两者不一致,可能导致主从数据差异或恢复失败。
- 原子性保障:两阶段提交确保事务要么全部生效,要么全部回滚,避免部分日志提交导致的不一致问题。
- 崩溃恢复能力:崩溃后,MySQL 通过 Redo Log 重放未完成的事务,并通过 Binlog 补偿主从同步的缺失数据。
WAL 与两阶段提交的关系?
- WAL 是基础:WAL 提供了日志先行的底层机制(如 Redo Log),确保数据变更的持久性。
- 两阶段提交是扩展:在 WAL 的基础上,两阶段提交解决了跨日志(Redo Log + Binlog)的一致性问题,是 WAL 在分布式场景(如主从复制)中的延伸。
MySQL 通过 WAL 技术 和 两阶段提交机制 的结合,既保证了单机事务的可靠性(Redo Log),又实现了跨节点的数据一致性(Binlog)。这一设计是 MySQL 高可用性和数据安全性的核心基础。
只用其中一种日志是否可以保证数据一致性?
MySQL 需要同时记录 Binlog 和 Redo log,因为它们在数据库中承担不同的职责,且存在本质差异。
- 如果仅依赖 Binlog,崩溃恢复时无法保证数据页一致性,无法高效恢复物理数据页。Binlog 记录的是逻辑操作,而数据页可能因未及时落盘或部分写入导致损坏。此时需通过 Redo log 的物理日志恢复数据页到一致状态。
- 如果仅依赖 Redo log,无法记录完整的数据库变更历史。Redo Log 是物理日志,采用循环写入方式,文件大小固定,会覆盖旧数据,没有完整的备份数据。
Binlog 是逻辑日志,无法直接用于物理层面的数据恢复;而 Redo log 是物理日志,无法直接用于逻辑复制或归档,两者缺一不可。
MySQL 如何查看 Binlog?
通过下面命令查看 Binlog 配置和文件位置:
SHOW VARIABLES LIKE "log_bin%"
再使用官方提供的 mysqlbinlog 工具查看:
mysqlbinlog binlog.xxx -d database_name | tail -n 100
MySQL Binlog 有那些格式?
MySQL Binlog主要有 STATEMENT、ROW、MIXED 三种格式。
STATEMENT
记录实际执行的 SQL 语句本身。
- 优点:
- 日志文件小:对于影响大量行的操作,只需记录一条 SQL 语句,日志量极小。
- 可读性强:日志内容是人类可读的 SQL 语句,便于理解数据库执行了哪些操作。
- 缺点:
- 主从不一致风险:如果 SQL 语句中使用了具有不确定性的函数如 NOW() 等,在主库和从库上执行时可能产生不同的结果,导致数据不一致。
适用场景:对数据一致性要求不高、能确保不使用不确定性函数且没有复杂上下文依赖的简单环境。现在已较少推荐使用。
ROW
记录每一行数据是如何被修改的。对于 INSERT,记录插入的整行新数据;对于 DELETE,记录被删除行的唯一标识或整行旧数据;对于 UPDATE,记录被修改行的唯一标识以及修改后的新数据。
- 优点:
- 数据一致性高:因为记录的是行的实际变化,而不是语句,所以不受不确定性函数影响。
- 更安全的复制:减少了因上下文差异导致复制中断或数据错误的风险。
- 缺点:
- 日志文件大:对于影响大量行的操作,需要记录这些行的变化,日志量会非常庞大。
适用场景:当前最推荐且主流的格式,尤其是在要求高数据一致性的场景,MySQL 5.7.7 及以后版本的默认格式就是 ROW。
MIXED
默认情况下使用 STATEMENT 格式记录 Binlog,但是当 MySQL 检测到语句可能引发不一致,如语句中包含不确定性函数时,会自动切换到 ROW 格式来记录该语句。
- 优点:
- 在保证绝大多数操作日志量小的同时,对少数有风险的语句使用 ROW 格式来确保安全。
- 缺点:
- 需要 MySQL 来判断何时切换,增加了内部逻辑的复杂性。
- 无法精确预知哪些操作会被记录为 ROW,导致日志大小预测和控制相对困难。
适用场景:希望平衡日志大小和数据一致性的场景。当绝大多数操作都是安全的(无不确定性函数),偶尔有风险操作时,MIXED 是一个折衷方案。