Mysql精进之路-持久化binlog与redo log写入机制

Dcr 1年前 ⋅ 874 阅读


binlog的写入机制
事务执行过程中,先把日志写到binlog cache,事务提交的时候,再把binlog cache写到binlog文件中.
一个事务的binlog是不能被拆开的,因此不论这个事务多大,也要确保一次性写入.这就涉及到了binlog cache的保存问题了.

系统给 binlog cache 分配了一片内存,每个线程一个,参数 binlog_cache_size 用于控制单个线程内 binlog cache 所占内存的大小。如果超过了这个参数规定的大小,就要暂存到磁盘。

事务提交的时候,执行器把 binlog cache 里的完整事务写入到 binlog 中,并清空 binlog cache。

在binlog cache写入binlog的过程中,并不是直接写磁盘,而是先将日志写入文件系统的page cache,正因为没有直接到磁盘所以速度较快.然后再从page cache将数据fsync持久化到磁盘.(一般情况下,这个步骤才占用磁盘IOPS)

持久化的时机通过参数sync_binlog控制:
1.sync_binlog=0的时候,表示每次提交事务都只write,不fsync;
2.sync_binlog=1到时候,表示每次提交事务都执行fsync;
3.sync_binlog=N(N>1)的时候,表示每次提交事务都write,但累计N个事务后才fsync;

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

但是,将 sync_binlog 设置为 N,对应的风险是:如果主机发生异常重启,会丢失最近 N 个事务的 binlog 日志。

redo log的写入机制
事务执行过程中,生成的redo log先写到redo log buffer.
如果事务执行期间MySQL发生异常重启,那这部分日志就丢了,由于事务并没有提交,所以这时日志丢了也不会有损失.

redo log与binlog存储流程类似也是从redo log buffer写到文件系统的page cache然后在持久化(fsync)到磁盘.
为了控制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;

InnoDB 有一个后台线程,每隔 1 秒,就会把 redo log buffer 中的日志,调用 write 写到文件系统的 page cache,然后调用 fsync 持久化到磁盘。

注意,事务执行中间过程的 redo log 也是直接写在 redo log buffer 中的,这些 redo log 也会被后台线程一起持久化到磁盘。也就是说,一个没有提交的事务的 redo log,也是可能已经持久化到磁盘的。

实际上,除了后台线程每秒一次的轮询操作外,还有两种场景会让一个没有提交的事务的 redo log 写入到磁盘中。

1.redo log buffer 占用的空间即将达到 innodb_log_buffer_size 一半的时候,后台线程会主动写盘。注意,由于这个事务并没有提交,所以这个写盘动作只是 write,而没有调用 fsync,也就是只留在了文件系统的 page cache。

2.并行的事务提交的时候,顺带将这个事务的 redo log buffer 持久化到磁盘。假设一个事务 A 执行到一半,已经写了一些 redo log 到 buffer 中,这时候有另外一个线程的事务 B 提交,如果 innodb_flush_log_at_trx_commit 设置的是 1,那么按照这个参数的逻辑,事务 B 要把 redo log buffer 里的日志全部持久化到磁盘。这时候,就会带上事务 A 在 redo log buffer 里的日志一起持久化到磁盘。

这里需要说明的是,我们介绍两阶段提交的时候说过,时序上 redo log 先 prepare, 再写 binlog,最后再把 redo log commit。

如果把 innodb_flush_log_at_trx_commit 设置成 1,那么 redo log 在 prepare 阶段就要持久化一次,因为有一个崩溃恢复逻辑是要依赖于 prepare 的 redo log,再加上 binlog 来恢复的。

每秒一次后台轮询刷盘,再加上崩溃恢复这个逻辑,InnoDB 就认为 redo log 在 commit 的时候就不需要 fsync 了,只会 write 到文件系统的 page cache 中就够了。

通常我们说 MySQL 的“双 1”配置,指的就是 sync_binlog 和 innodb_flush_log_at_trx_commit 都设置成 1。也就是说,一个事务完整提交前,需要等待两次刷盘,一次是 redo log(prepare 阶段),一次是 binlog。

全部评论: 0

    我有话说: