MySQL

MySQL-16.binlog和redo log写入机制以及性能

2019-08-15  本文已影响23人  王侦

WAL 机制:只要 redo log 和 binlog 保证持久化到磁盘,就能确保 MySQL 异常重启后,数据可以恢复。

1.binlog 的写入机制

一个事务的 binlog 是不能被拆开的,因此不论这个事务多大,也要确保一次性写入。这就涉及到了 binlog cache 的保存问题。系统给 binlog cache 分配了一片内存,每个线程一个,参数 binlog_cache_size 用于控制单个线程内 binlog cache 所占内存的大小。如果超过了这个参数规定的大小,就要暂存到磁盘。


每个线程有自己 binlog cache,但是共用同一份 binlog 文件。

write 和 fsync 的时机,是由参数 sync_binlog 控制的:

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

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

2.redo log 的写入机制

事务在执行过程中,生成的redo log 是要先写到 redo log buffer 的。redo log buffer里面的内容,不是每次生成后都要直接持久化到磁盘。

如果事务执行期间 MySQL 发生异常重启,那这部分日志就丢了。由于事务并没有提交,所以这时日志丢了也不会有损失。

事务还没提交的时候,redo log buffer 中的部分日志有可能被持久化到磁盘。



redo log 可能存在的三种状态:

为了控制 redo log 的写入策略,InnoDB 提供了 innodb_flush_log_at_trx_commit 参数,它有三种可能取值:

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

让一个没有提交的事务的 redolog 写入到磁盘中的三种情况:

两阶段提交:时序上 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。

从 MySQL 看到的 TPS 是每秒两万的话,每秒就会写四万次磁盘。但是,用工具测试出来,磁盘能力也就两万左右,怎么能实现两万的 TPS?

Ans:组提交(group commit)机制


三个并发事务 (trx1, trx2, trx3) 在 prepare 阶段,都写完 redo log buffer,持久化到磁盘的过程,对应的 LSN 分别是 50、120 和 160。

所以,一次组提交里面,组员越多,节约磁盘 IOPS 的效果越好。但如果只有单线程压测,那就只能老老实实地一个事务对应一次持久化操作了。

在并发更新场景下,第一个事务写完 redo log buffer 以后,接下来这个 fsync 越晚调用,组员可能越多,节约 IOPS 的效果就越好。

为了让一次 fsync 带的组员更多,MySQL 有一个很有趣的优化:拖时间。



实际上,写 binlog 是分成两步的:

MySQL 为了让组提交的效果更好,把 redo log 做 fsync 的时间拖到了步骤 1 之后。



binlog 也可以组提交了。在执行中第 4 步把 binlog fsync 到磁盘时,如果有多个事务的 binlog 已经写完了,也是一起持久化的,这样也可以减少 IOPS 的消耗。不过通常情况下第 3 步执行得会很快,所以 binlog 的 write 和 fsync 间的间隔时间短,导致能集合到一起持久化的 binlog 比较少,因此 binlog 的组提交的效果通常不如 redo log 的效果那么好。

如果你想提升 binlog 组提交的效果,可以通过设置binlog_group_commit_sync_delay 和
binlog_group_commit_sync_no_delay_count 来实现。

这两个条件是或的关系,也就是说只要有一个满足条件就会调用 fsync。

WAL 机制主要得益于两个方面:

3.性能瓶颈在 IO上

如果 MySQL 现在出现了性能瓶颈,而且瓶颈在 IO上,可以通过哪些方法来提升性能呢?

Ans:

在什么时候会把线上生产库设置成“非双 1”?
Ans:

一般情况下,把生产库改成“非双 1”配置,是设置innodb_flush_logs_at_trx_commit=2、sync_binlog=1000。

4.相关问题

Q1.执行一个 update 语句以后,我再去执行 hexdump 命令直接查看 ibd 文件内容,为
什么没有看到数据有改变呢?

Ans:

Q2.为什么 binlog cache 是每个线程自己维护的,而 redo log buffer 是全局共用的?

Ans:

Q3.事务执行期间,还没到提交阶段,如果发生 crash 的话,redo log 肯定丢了,这会不
会导致主备不一致呢?

Ans:

Q4.如果 binlog 写完盘以后发生 crash,这时候还没给客户端答复就重启了。等客户端再
重连进来,发现事务已经提交成功了,这是不是 bug?

Ans:

上一篇下一篇

猜你喜欢

热点阅读