- 本文是笔记类型文章,只有结论没有证明,可用于复习巩固知识,不能用于新知识的学习。如有错误,恳请指正,不胜感谢。
- 转载请于文首标明出处:【MySQL】日志简述 - 掘金 (juejin.cn)
- 文章仍未完工,内容会逐步完善
系列文章:
- 【MySQL】逻辑架构简述 - 掘金 (juejin.cn)
- 【MySQL】索引简述 - 掘金 (juejin.cn)
- 【MySQL】并发,事务与锁简述 - 掘金 (juejin.cn)
- 【MySQL】日志简述 - 掘金 (juejin.cn)
binlog
binlog 称为二进制日志(Binary Log),是 MySQL 服务层的日志,用于记录执行的写操作语句,属于逻辑日志。只有事务提交后才会写 binlog,当事务提交但是数据没有落盘时如果发生宕机,则在重启之后可以通过 mysqlbinlog 和 binlog 重做事务。由于是 MySQL 服务层的日志,因此可以对所有存储引擎提供支持。
由于 MySQL 服务层不负责存储,落盘操作由存储引擎控制,因此服务层必须通过 binlog 来保证 DML 执行的持久性。
配置
- 启用与关闭:可以通过在配置文件中配置
log-bin=[log_file_name]或者执行SET SQL_LOG_BIN=1来开启 binlog。删除log-bin配置项或执行SET SQL_LOG_BIN=0可以关闭 binlog。 - 写入方式:追加写。文件大小达到配置阈值或执行
FLUSH LOGS时会切换到新的 binlog 文件。旧文件通常需要手动或按配置策略(expire_logs_days)清除。 - 写入时机:写入时机由
sync_binlog控制。sync_binlog=0,MySQL 不控制 binlog 的刷新,由操作系统自行在空闲时刷入磁盘。sync_binlog=N,N 为正整数,表示每 N 次事务,MySQL 手动将 binlog 刷入磁盘。 因此sync_binlog=1可以带来最高的一致性,通常我们也是选择配置为 1。
创建
以下情况会重新创建一个 binlog 文件:
- MySQL 重启。
- 使用
flush log命令。 - 文件大小超过
max_binlog_size,max_binlog_size默认为最大值 1G,最小值为 4k。但是当单次 DML 大小超过 1G 时,由于不会将单次操作拆分为多个文件存储,因此会出现 binlog 大小大于max_binlog_size的情况。
内容
记录了对数据库执行的数据修改操作本身。
主要包含三种格式:
- Statement :记录原始的 SQL 语句本身(如
INSERT INTO users (name) VALUES ('Alice');,UPDATE orders SET status='shipped' WHERE id=123;)。 - Row : 记录被修改行的具体变更内容。
- 对于
INSERT:记录新插入行的所有字段值。 - 对于
DELETE:记录被删除行的唯一标识(通常是主键)或者完整行的旧值(取决于配置)。 - 对于
UPDATE:记录被修改行的唯一标识(主键)以及修改后各字段的新值(ROW格式默认只记新值),有时也包含修改前的标识/值(如果配置了需要)。
- 对于
- Mixed : MySQL 动态选择使用 STATEMENT 还是 ROW 格式来记录操作。通常安全的操作用 STATEMENT,可能引起主从数据不一致的操作自动切换到 ROW。
此外,还包括事务的提交信息(BEGIN/COMMIT/ROLLBACK 等)、数据库结构变更(CREATE/DROP/ALTER TABLE 等 DDL 语句)。
用途
- 主从复制 (Replication):从库(Slave)读取主库(Master)的 binlog 并在本地重放,以达到数据同步的目的。
- 基于时间点或位置的恢复 (Point-in-Time Recovery, PITR):利用全量备份+binlog 重放,将数据库恢复到历史某个特定时间点或日志位置的状态。
- 数据审计 (Auditing):分析 binlog 可以追踪所有对数据库的修改记录。
redo log
redo log 又称重做日志,是 InnoDB 存储引擎的日志,记录了在 InnoDB 存储引擎层发生的、对数据库数据页所做的物理修改,属于物理日志。redo log 仅记录事务对哪些数据页做了哪些修改,采用顺序写的方式,因此写入速度非常快,不会对性能造成太大影响。当事务执行提交之后,如果数据还未落盘数据库就宕机了,重启后 InnoDB 会自动根据 redo log 对丢失的事务进行重做。
为什么事务提交时不直接将数据落盘呢?是由于对于数据的修改很多时候都是随机的,而 InnoDB 默认页大小是 16K,因此如果一个修改涉及到多个页,此时需要进行多次随机 IO。修改完之后又需要刷入磁盘,又需要进行多次随机 IO,速度非常慢。因此对于事务的写操作通常都会将修改缓存到内存中,等待空闲时写入。
InnoDB 的 WAL(Write Ahead Log)技术的产物就是 redo log,对于写操作,永远都是日志先行,先写入 redo log 确保一致性之后,再对修改数据进行落盘。
log buffer
redo log 除了在磁盘上的文件实体之外,还在内存中有一块日志缓冲区 log buffer。如果每次进行修改都直接写磁盘的话,效率并不高,因此使用一个缓冲区先在内存中记录写操作,等到多次写操作发生,再一次写入磁盘。通过将随机写优化为顺序写,可以极大提高 redo log 的性能。redo log buffer 有三种落盘策略,由 innodb_flush_log_at_trx_commit 控制:
- 0:按秒写,按秒刷。每秒调用
write()写入 OS Buffer 并调用flush()刷入磁盘。 - 1:实时写,实时刷。每次事务提交都调用
write()写入 OS Buffer 并调用flush()刷入磁盘。 - 2:实时写,延迟刷。每次事务提交都调用
write()写入 OS Buffer,但每秒调用flush()刷入磁盘。
因此采用延迟写策略时系统运行速度最快,但是发生意外会有 1s 的数据丢失。根据使用场景选择即可。
write和flush的概念是由于存在 OS Buffer,OS Buffer 是操作系统为了协调内存与磁盘的速度而提供的写缓冲区。因此我们单纯的write()并无法保证数据即刻落盘,因此 OS 提供flush()系统调用来触发立刻刷新缓冲。
内容
包含一组被称为 redo records 或 redo entries 的记录。
每条记录大致包含:
- 被修改的
表空间标识 (Tablespace ID) - 被修改的
数据页编号 (Page Number) - 数据页内修改发生的
起始位置(Offset within the page) - 修改的具体内容(
字节变化) - (可能还有其他元数据如 LSN - Log Sequence Number)
它记录的是一种物理行为,比如“在表空间 X 的第 Y 号页面的偏移量 Z 处,将值从 A 改成了 B”,或者“向第 Y 号页面的空闲空间列表添加了一个记录”等。它不关心是哪个 SQL 语句导致了这个修改,也不直接关联到具体的行。
用途
- 崩溃恢复 (Crash Recovery) : 这是 redo log 的核心作用。当 MySQL 异常崩溃时,重启后 InnoDB 会检查 redo log。那些在崩溃前已经写入 redo log(即事务已提交或部分提交)但尚未实际写入数据文件(
*.ibd)的修改,会被重做 (redo) 一次,从而保证事务的持久性 (Durability) 。即使数据页在内存(Buffer Pool)中被修改了但没刷盘,只要对应的 redo log 落盘了,数据就不会丢失。 - Write-Ahead Logging (WAL) 策略的体现:确保在数据页实际修改被写入磁盘之前,必须先将其物理改动的描述(redo log)持久化到磁盘。
循环写入
redo log 采用循环写入的模式,当前写入位置有一个 write_pos 指针,当前已落盘的数据日志结尾有一个 check_point 指针,也就是位于安全点之前日志可以被删除,位于安全点之后的数据不可被删除。当 write_pos 与 check_point 相遇时,redo log 空间不够,此时需要立刻将部分数据落盘,后移 check_point 以保证 redo log 的使用。