MySQL 三大日志binlog、redo log 和 undo log深度解析1. redo logredo log 的核心定位与工作原理redo log 的刷盘时机innodb_flush_log_at_trx_commit0innodb_flush_log_at_trx_commit1innodb_flush_log_at_trx_commit2日志文件组与环形写入2. binlogbinlog 的核心定位与本质特征binlog 的记录格式binlog 的写入与刷盘机制刷盘时机由 sync_binlog 参数控制3. 两阶段提交redo log 与 binlog 的一致性难题两阶段提交的核心逻辑4. undo logundo log 的核心作用undo log 与 MVCC 的深度绑定在 MySQL 数据库的运行体系中日志系统是保障数据可靠性、一致性和可恢复性的核心组件。其中redo log重做日志、binlog归档日志和 undo log回滚日志作为三大核心日志各自承担着不同的关键角色1. redo logredo log 的核心定位与工作原理redo log是 InnoDB 存储引擎独有的物理日志是 MySQL 具备崩溃恢复能力的核心比如MySQL实例挂了或宕机了重启时InnoDB存储引擎会使用redo log恢复数据保证数据的持久性与完整性要理解redo log的工作流程需先明晰 MySQL 的数据读写机制MySQL 中数据以 “页” 为基本单位默认 16KB当查询数据时会先从磁盘加载对应数据页到内存中的Buffer Pool缓冲池后续查询优先从Buffer Pool命中未命中再读取磁盘以此减少磁盘 IO 开销提升读写性能。数据更新操作同样遵循这一逻辑若Buffer Pool中存在待更新的数据页会直接在内存中修改同时将 “数据页的修改行为” 记录到redo log buffer重做日志缓存再通过特定策略刷盘到redo log文件中。相较于直接将修改后的数据页刷盘是随机 IO性能极低写入 redo log是顺序 IO的速度极快既保证了更新操作的高效性又通过日志留存了恢复数据的依据。redo log 的刷盘时机redo log 的刷盘策略直接决定了数据安全性与数据库性能的平衡InnoDB 通过innodb_flush_log_at_trx_commit参数提供了三种核心策略同时辅以后台线程的自动刷盘机制0设置为 0 的时候表示每次事务提交时不进行刷盘操作1设置为 1 的时候表示每次事务提交时都将进行刷盘操作默认值2设置为 2 的时候表示每次事务提交时都只把 redo log buffer 内容写入 page cache另外InnoDB存储引擎有一个后台线程每隔1秒就会把redo log buffer中的内容写到文件系统缓存page cache然后调用fsync刷盘。也就是说一个没有提交事务的redo log记录也可能会刷盘除了后台线程每秒1次的轮询操作还有一种情况当redo log buffer占用的空间即将达到innodb_log_buffer_size一半的时候后台线程会主动刷盘避免缓存溢出innodb_flush_log_at_trx_commit0为0时如果MySQL挂了或宕机可能会有1秒数据的丢失innodb_flush_log_at_trx_commit1为1时 只要事务提交成功redo log记录就一定在硬盘里不会有任何数据丢失。如果事务执行期间MySQL挂了或宕机这部分日志丢了但是事务并没有提交所以日志丢了也不会有损失。innodb_flush_log_at_trx_commit2为2时 只要事务提交成功redo log buffer中的内容只写入文件系统缓存page cache。如果仅仅只是MySQL挂了不会有任何数据丢失但是宕机可能会有1秒数据的丢失。日志文件组与环形写入磁盘上的 redo log 并非单个文件而是以 “日志文件组” 的形式存在组内每个日志文件大小固定可通过innodb_log_file_size配置通常建议设置为 1-4GB且数量由innodb_log_files_in_group默认 2指定。redo log的写入采用 “环形数组” 模式从第一个文件的起始位置开始顺序写入写满一个文件后切换到下一个直到写满最后一个文件再回到第一个文件从头覆盖。这种设计既保证了日志写入的顺序性顺序 IO又通过固定大小的文件组控制了磁盘占用同时 InnoDB 会记录 “检查点”checkpoint和 “当前写入点”write poswrite pos当前正在写入的位置推进到文件末尾则回到头部checkpoint表示已完成数据刷盘、可以覆盖的日志位置两者之间的区域为 “可写入日志区”若write pos追上checkpointInnoDB 会暂停写入先推进checkpoint将已记录的日志对应的脏页刷盘再继续写入。2. binlogbinlog 的核心定位与本质特征不管用什么存储引擎只要发生了表数据更新都会产生binlog日志与redo logInnoDB 专属、物理日志不同binlog是 MySQL Server 层的逻辑日志不依赖存储引擎 —— 无论使用 InnoDB、MyISAM 还是其他引擎只要发生表数据变动都会生成binlog。其核心价值在于数据备份通过binlog可以恢复指定时间段内的数据集群同步主备、主从、主主架构中从库通过解析主库的 binlog 同步数据保证集群数据一致性数据审计可通过 binlog 追溯数据修改的历史操作。binlog的特性体现在它记录的是修改数据的 “原始逻辑”而非数据页的物理变化例如 “给 ID2 的行的 c 字段加 1”“插入一条 id10、nametest 的记录”而非 “某个数据页的第 N 个字节从 X 改为 Y”。同时binlog 采用顺序写入模式避免了随机 IO保证了日志写入的高效性。可以说MySQL数据库的数据备份、主备、主主、主从都离不开binlog需要依靠binlog来同步数据保证数据一致性。binlog 的记录格式binlog通过binlog_format参数支持三种记录格式分别适配不同的业务场景statementrowmixed1、statement 格式记录 SQL 语句原文逻辑直接记录执行的 SQL 语句例如执行update T set update_timenow() where id1binlog 会完整记录这条 SQL优点日志体积小写入速度快占用磁盘空间少缺点存在 “数据不一致风险”—— 如 SQL 中包含now()、rand()等非确定性函数或依赖会话变量、存储过程时从库执行这条 SQL 会得到与主库不同的结果2、row 格式记录数据的实际修改为了解决 statement 格式 的问题我们需要指定为row逻辑不记录 SQL 语句而是记录 “修改前的行数据” 和 “修改后的行数据”例如上述 update 语句会记录 “id1 的行update_time 修改前为 2024-01-01 10:00:00修改后为 2024-01-01 10:01:00”优点完全保证数据同步的一致性即使包含非确定性函数也不会出错是数据恢复和同步的最可靠格式缺点日志体积大写入和解析的 IO 开销更高会增加数据库负担所以就有了一种折中的方案指定为mixed记录的内容是前两者的混合3、金选mixed 格式混合自适应模式逻辑MySQL 自动判断 SQL 语句的类型 —— 若语句是 “确定性的”如update T set c1 where id2则用 statement 格式若语句是 “非确定性的”如包含now ()、rand ()则自动切换为 row 格式优点兼顾 statement 的高性能和 row 的一致性是折中方案缺点日志格式不固定增加了数据恢复和问题排查的复杂度生产环境中建议优先设置为row格式 —— 虽然牺牲了部分性能但数据一致性是集群架构的核心要求且现代硬件的 IO 能力足以承载row格式的开销仅在非核心、低并发的单机场景下可考虑mixed或statement格式。binlog 的写入与刷盘机制binlog的写入时机也非常简单事务执行过程中先把日志写到binlog cache事务提交的时候再把binlog cache写到binlog文件中。因为一个事务的binlog不能被拆开无论这个事务多大也要确保一次性写入所以系统会给每个线程分配一个块内存作为binlog cache。我们可以通过binlog_cache_size参数控制单个线程 binlog cache 大小如果存储内容超过了这个参数就要暂存到磁盘Swap避免事务中断。binlog日志刷盘流程如下上图 binlog 的刷盘分为 “write” 和 “fsync” 两步write将 binlog cache 内容写入 page cache文件系统缓存未持久化到磁盘速度快fsync将 page cache 中的内容刷盘到物理磁盘是真正的持久化操作。刷盘时机由 sync_binlog 参数控制上面介绍到binlog 的刷盘分为 “write” 和 “fsync” 两步sync_binlog参数决定了 fsync 的触发时机0默认事务提交时仅执行 writefsync 由操作系统自行决定通常 30 秒一次性能最高但服务器宕机会丢失 page cache 中的所有 binlog1事务提交时同时执行 write 和 fsyncbinlog 实时持久化数据最安全但性能略有损耗NN1事务提交时执行 write累积 N 个事务后触发一次 fsync兼顾性能与安全但宕机会丢失最近 N 个事务的 binlog。核心业务集群中建议设置sync_binlog1若存在 IO 瓶颈可将 N 设置为 100-1000根据业务容忍度调整同时配合磁盘阵列、SSD 等硬件优化 IO 性能。3. 两阶段提交redo log 与 binlog 的一致性难题虽然它们都属于持久化的保证但是侧重点不同redo log保障 InnoDB 的崩溃恢复binlog保障集群数据同步但若两者的写入逻辑不一致在执行更新语句过程会记录redo log与binlog两块日志以基本的事务为单位redo log在事务执行过程中可以不断写入而binlog只有在提交事务时才写入所以redo log与binlog的写入时机不一样严重时会导致 “数据恢复后与集群同步的数据不一致” 的问题以update T set c1 where id2为例若redo log已刷盘但binlog写入时发生异常则原库重启后通过redo log恢复数据c1但从库同步binlog时因缺失这条记录数据仍为 c0最终主从数据不一致若binlog已写入但redo log未刷盘原库重启后无法恢复该修改c0而从库同步binlog后 c1同样导致不一致。两阶段提交的核心逻辑为了解决两份日志之间的逻辑一致问题InnoDB存储引擎使用两阶段提交方案。原理很简单将redo log的写入拆成了两个步骤prepare和commit这就是两阶段提交。执行事务的修改操作将数据页修改写入Buffer Pool同时记录undo log写入redo log将redo log标记为prepare阶段写入binlog并将binlog刷盘根据sync_binlog策略再次修改redo log将其标记为commit阶段事务完成。使用两阶段提交后写入binlog时发生异常也不会有影响因为MySQL根据redo log日志恢复数据时发现redo log还处于prepare阶段并且没有对应binlog日志就会回滚该事务。再看一个场景redo log设置commit阶段发生异常那会不会回滚事务呢并不会MySQL 重启后读取redo log发现事务处于prepare阶段但能通过事务 ID 找到对应的binlog判定事务完整自动将redo log标记为commit恢复数据避免不一致。两阶段提交的本质是 “将两个独立的日志写入操作绑定为一个原子事务”牺牲了极少量的性能因为饿多一次redo log写入但保证了全局数据一致性是 MySQL 分布式事务的基础。4. undo logundo log 的核心作用事务的原子性要求 “要么全部执行要么全部回滚”而undo log正是实现回滚的核心所有事务的修改操作执行前都会先将 “数据修改前的状态” 记录到undo log中例如更新操作会记录旧值插入操作会记录 “删除标记”且undo log会优先于数据持久化到磁盘。当事务执行过程中出现异常MySQL 会读取undo log中的历史数据将数据页恢复到修改前的状态保证未完成的事务不会对数据库造成永久性影响undo log 与 MVCC 的深度绑定除了事务回滚undo log还是 InnoDB 实现 MVCC多版本并发控制的核心依赖。MVCC 的目标是 “读写不阻塞提高并发性能”其实现依赖三大组件隐藏字段每行数据包含DB_TRX_ID修改该记录的事务 ID、DB_ROLL_PTR指向 undo log 的指针Read View事务启动时生成的 “可见性视图”记录当前活跃的事务 IDundo log存储数据的历史版本通过DB_ROLL_PTR形成版本链。当事务读取数据时InnoDB 通过DB_TRX_ID和 Read View 判断数据是否可见若不可见则通过DB_ROLL_PTR遍历undo log的版本链找到事务可见的历史版本。这一机制让不同事务能看到不同版本的数据避免了读写锁的冲突是 InnoDB 高并发的关键。undo log会随着事务的提交逐步清理如事务提交后其生成的undo log若不再被其他事务的 MVCC 读取会被后台线程删除避免磁盘空间无限膨胀同时undo log也以 “段” 的形式存储在 InnoDB 的表空间中可通过innodb_undo_tablespaces等参数优化其存储布局