当前位置: 代码迷 >> 综合 >> 【mysql45讲】redo log & binlog
  详细解决方案

【mysql45讲】redo log & binlog

热度:5   发布时间:2024-02-20 14:41:28.0

1.WAL

WAL 的全称是 Write-Ahead Logging,它的关键点就是先写日志(写日志也是写磁盘,不过是顺序写,所以快),再写磁盘。
只要 redo log 和 binlog 保证持久化到磁盘,就能确保 MySQL 异常重启后,数据可以恢复。

2.redo log介绍

2.1 redo log循环写

redo log属于InnoDB日志。
redo log 是固定大小的,比如可以配置为一组 4 个文件,每个文件的大小是 1GB,那么总共就可以记录 4GB 的操作。从头开始写,写到末尾就又回到开头循环写。
示例如图:
在这里插入图片描述
write pos 是当前记录的位置,一边写一边后移,写到第 3 号文件末尾后就回到 0 号文件开头。checkpoint 是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。write pos 和 checkpoint 之间的是“粉板”上还空着的部分,可以用来记录新的操作。如果 write pos 追上 checkpoint,表示“粉板”满了,这时候不能再执行新的更新,得停下来先擦掉一些记录,把 checkpoint 推进一下。

2.2 crash-safe

即使数据库异常重启,之前的记录也不会丢失,这就是redo log有crash-safe能力。

innodb_flush_log_at_trx_commit 这个参数设置成 1 的时候,表示每次事务的 redo log都直接持久化到磁盘。这个参数我建议你设置成 1,这样可以保证 MySQL 异常重启之后数据不丢失。

2.3 redo log的写入机制

redo log可能存在的三种状态如下:
1.存在 redo log buffer 中,物理上是在 MySQL 进程内存中;
2.写到磁盘 (write),但是没有持久化(fsync),物理上是在文件系统的 page cache 里;
3.持久化到磁盘,对应的是 hard disk。

日志写到 redo log buffer 是很快的,wirte 到 page cache 也差不多,但是持久化到磁盘的速度就慢多了。
为了控制 redo log 的写入策略,InnoDB 提供了 innodb_flush_log_at_trx_commit 参数,它有三种可能取值:
1.设置为 0 的时候,表示每次事务提交时都只是把 redo log 留在 redo log buffer 中 ;
2.设置为 1 的时候,表示每次事务提交时都将 redo log 直接持久化到磁盘;
3.设置为 2 的时候,表示每次事务提交时都只是把 redo log 写到 page cache。

没有提交事务的redo log被写入到磁盘的三种情况:
1.InnoDB 有一个后台线程,每隔 1 秒,就会把 redo log buffer 中的日志,调用 write 写到文件系统的 page cache,然后调用 fsync 持久化到磁盘。
2.redo log buffer 占用的空间即将达到 innodb_log_buffer_size 一半的时候,后台线程会主动写盘。注意,由于这个事务并没有提交,所以这个写盘动作只是 write,而没有调用 fsync,也就是只留在了文件系统的 page cache。
3.并行的事务提交的时候,顺带将这个事务的 redo log buffer 持久化到磁盘。假设一个事务 A 执行到一半,已经写了一些 redo log 到 buffer 中,这时候有另外一个线程的事务 B 提交,如果 innodb_flush_log_at_trx_commit 设置的是 1,那么按照这个参数的逻辑,事务 B 要把 redo log buffer 里的日志全部持久化到磁盘。这时候,就会带上事务 A 在 redo log buffer 里的日志一起持久化到磁盘。

3.binlog介绍

3.1 binlog

binlog属于Server层的日志(归档日志)。
它可以追加写入,不会覆盖之前的。
只依靠binlog是没有crash-safe能力的。

sync_binlog 这个参数设置成 1 的时候,表示每次事务的 binlog 都持久化到磁盘。这个参数我也建议你设置成 1,这样可以保证 MySQL 异常重启之后 binlog 不丢失。

3.2 binlog的写入机制

事务执行过程中,先把日志写到 binlog cache,事务提交的时候,再把 binlog cache 写到 binlog 文件中。
一个事务的 binlog 是不能被打断的,一个事务的binlog必须连续写。这就涉及到了 binlog cache 的保存问题。系统给 binlog cache 分配了一片内存,每个线程一个,
参数 binlog_cache_size 用于控制单个线程内 binlog cache 所占内存的大小。如果超过了这个参数规定的大小,就要暂存到磁盘。事务提交的时候,执行器把 binlog cache 里的完整事务写入到 binlog 中,并清空 binlog cache。
在这里插入图片描述
可以看到,每个线程有自己 binlog cache,但是共用同一份 binlog 文件。
图中的 write,指的就是指把日志写入到文件系统的 page cache,并没有把数据持久化到磁盘,所以速度比较快。
图中的 fsync,才是将数据持久化到磁盘的操作。一般情况下,我们认为 fsync 才占磁盘的 IOPS。

write 和 fsync 的时机,是由参数 sync_binlog 控制的:
sync_binlog=0 的时候,表示每次提交事务都只 write,不 fsync;
sync_binlog=1 的时候,表示每次提交事务都会执行 fsync;
sync_binlog=N(N>1) 的时候,表示每次提交事务都 write,但累积 N 个事务后才 fsync。(如果主机发生异常重启,会丢失最近 N 个事务的 binlog 日志。)

因此,在出现 IO 瓶颈的场景里,将 sync_binlog 设置成一个比较大的值,可以提升性能。在实际的业务场景中,考虑到丢失日志量的可控性,一般不建议将这个参数设成 0,比较常见的是将其设置为 100~1000 中的某个数值。

4.redo log和binlog的区别

1.redo log是InnoDB引擎特有的,而binlog属于Server层所有引擎都能用。
2.redo log是物理日志,记录的是“在某个数据页上实现了什么修改”。binlog是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ”。
3.redo log循环写,空间固定会用完。binlog追加写,文件写到一定大小会切换下一个,不会覆盖以前的日志。

5.两阶段提交(使用redo log和binlog)

5.1 两阶段提交介绍

比如执行下面这条语句
mysql> update T set c=c+1 where ID=2;
过程如图
在这里插入图片描述
写入redo log分为:prepare和commit两个步骤
两阶段提交过程:写入redo log(处于prepare状态)->写binlog到磁盘->redo log commit
两阶段提交目的:让两份日志逻辑一致

5.2 两阶段提交如何保证数据库异常重启时数据完整性

5.2.1 举例

在这里插入图片描述
A时刻异常重启:由于redo log未提交,binlog也没写,这个事务会回滚。
B时刻异常重启:redo log写完未提交,binlog已写。对应以下的2(a)情况,崩溃恢复时事务会提交。

崩溃恢复时的判断规则:
如果 redo log 里面的事务是完整的,也就是已经有了 commit 标识,则直接提交;
如果 redo log 里面的事务只有完整的 prepare,则判断对应的事务 binlog 是否存在并完整:
a. 如果是,则提交事务;b. 否则,回滚事务。

5.2.2 其他问题

1.怎样知道binlog完整性
一个事务的 binlog 是有完整格式的:statement 格式的 binlog,最后会有 COMMIT;row 格式的 binlog,最后会有一个 XID event。

另外,在 MySQL 5.6.2 版本以后,还引入了 binlog-checksum 参数,用来验证 binlog 内容的正确性。对于
binlog 日志由于磁盘原因,可能会在日志中间出错的情况,MySQL 可以通过校验 checksum 的结果来发现。所以,MySQL
还是有办法验证事务 binlog 的完整性的。

2.怎么关联redo log和binlog
redo log和binlog有个共同的字段XID。崩溃恢复的时候会按顺序扫描redo log:
碰到有prepare,commit的redo log,直接提交;
碰到有prepare,没commit的redo log,就拿着这个XID去找binlog。

3.只有binlog为什么不支持数据恢复
在这里插入图片描述
在这里插入图片描述

6.MySQL 的“双 1”配置

sync_binlog 和 innodb_flush_log_at_trx_commit 都设置成 1。也就是说,一个事务完整提交前,需要等待两次刷盘,一次是 redo log(prepare 阶段),一次是 binlog。

7.组提交机制

7.1 日志逻辑序列号LSN

LSN 是单调递增的,用来对应 redo log 的一个个写入点。每次写入长度为 length 的 redo log, LSN 的值就会加上 length。LSN 也会写到 InnoDB 的数据页中,来确保数据页不会被多次执行重复的 redo log。

7.2 redo log组提交

举例如图:三个并发事务 (trx1, trx2, trx3) 在 prepare 阶段,都写完 redo log buffer,持久化到磁盘的过程,对应的 LSN 分别是 50、120 和 160。
在这里插入图片描述

在这里插入图片描述
所以,一次组提交里面,组员越多,节约磁盘 IOPS 的效果越好。但如果只有单线程压测,那就只能老老实实地一个事务对应一次持久化操作了。在并发更新场景下,第一个事务写完 redo log buffer 以后,接下来这个 fsync 越晚调用,组员可能越多,节约 IOPS 的效果就越好。

7.3 mysql做的优化,使binlog也可以组提交

吃完饭再学

  相关解决方案