04_binlog 日志
约 1256 字大约 4 分钟
2024-08-10
MySQL binlog 是什么
之前我们说的 redo log 是一种偏向物理性质的重做日志,记录的是类似这样的东西:“对哪个数据页中的什么记录,做了什么修改”
redo log 是属于 InnoDB 存储引擎特有的东西
binlog 叫做归档日志,是一种偏向逻辑性的日志,类似于“对 user 表中 id = 10 的一行数据做了更新操作,更新以后的值是什么”
binlog 不是 InnoDB 存储引擎特有的日志文件,是属于 MySQL Server 的日志文件
提交事务的时候,同时会写入 binlog
前面我们讲到,在提交事务的时候会把 redo log 日志写到磁盘文件,其实在提交事务的时候,还会把这次更新对应的 binlog 日志写到磁盘文件,如下图所示

上图增加了执行器这个组件,他负责与 InnoDB 存储引擎进行交互,包括从磁盘加载数据到 Buffer poll 中进行缓存、写入 undo 日志、更新 Buffer Pool 里的数据、写入 redo log buffer、redo log 刷入磁盘、写 binlog 等等
执行器是非常核心的一个组件,负责和存储引擎配合完成一个 SQL 语句在磁盘与内存层面的全部数据更新操作
上图中把一次更新语句的执行拆分成了两个阶段,1、2、3、4 步骤属于执行更新语句阶段,5、6 属于提交事务阶段
binlog日志刷盘策略分析
对于 binlog 日志也有不同的刷盘策略,sync_binlog 参数控制 binlog 的刷盘策略,默认值是 0
sync_binlog 参数值为 0 时,binlog 日志写入磁盘的时候不是直接进入磁盘文件,而是进入 os cache 内存缓存,与之前分析一样,如果此时机器宕机,那么在 os cache 里的 binlog 日志是会丢失的,如下图

sync_binlog 参数值为 1 时,会强制在提交事务的时候,将 binlog 直接写入到磁盘文件里去,这样在提交事务之后,哪怕是机器宕机,磁盘上的 binlog 日志也不会丢失

基于 binlog 和 redo log 完成事务的提交
当我们把 binlog 写入磁盘文件之后,接着就会完成最终的事务提交
此时会把本次更新的 binlog 文件名称和 binlog 日志在文件里的位置,都写入到 redo log 日志文件,同时在 redo log 日志文件里写入一个 commit 标记
完成这些操作之后才算最终完成了事务的提交,如下图

最后在 redo log 中写入 commit 标记有什么意义呢?
commit 标记的作用是用来保持 redo log 日志与 binlog 日志一致的
假设在提交事务的时候有上图中 5、6、7 三个步骤,这三个步骤都执行完了才算是提交了事务,在刚完成步骤 5 的时候,mysql 宕机了,此时会怎么样?
由于 redo 日志里没有 commit 标记,此次事务可以判定为不成功,不会造成 redo 日志有此次更新日志,binlog 日志没有这次更新日志,出现数据不一致的情况
完成步骤 6,也就是 binlog 写入磁盘了,此时 mysql 宕机也是同理,redo log 中没有最终的 commit 标记,此次事务提交是失败的
总之就是 redo log 中写入最终的事务 commit 标记了,事务才算是提交成功,此时 redo log、binglog 里都有本次更新对应的日志,是完全一致的
后台 IO 线程随机将内存更新后的脏数据刷回磁盘
假设我们现在已经提交事务了,此次更新已经把内存 buffer pool 中的缓存数据更新了,但是磁盘上的数据还是旧值
所以 MySQL 有一个后台 IO 线程,会在之后的某个时间里,随机的把内存 buffer pool 中修改后的脏数据给刷回到磁盘上的数据文件中

当 IO 线程把 buffer pool 里修改后的脏数据刷回到磁盘之后,磁盘上的数据才会跟内存里一样
在 IO 线程把脏数据刷回磁盘前,哪怕 MySQL 宕机也没关系,因为重启之后会根据 redo 日志恢复之前提交事务做过的修改到内存里去
等到适当时机,IO 线程会把这个修改后的数据刷到磁盘文件里去