事务执行过程中,binlog 首先会被写到 binlog cache 中;事务提交的时候,再讲binlog cache 写到 binlog 文件中。一个事务的 binlog 是原子的,无论多大都需要保证完整性。
系统为每个客户端线程分配一个 binlog cache,其大小由 binlog_cache_size 控制。如果binlog cache 超过阀值,就会临时持久化到磁盘。当事务提交的时候,再将 binlog cache 中完整的事务持久化到磁盘中,并清空 binlog cache。
从上面可以看出,每个客户端线程都有自己独立的 binlog cache,但是会共享一份 binlog files。
上面的 write 是指把binlog cache 写到文件系统的 page cache,并没有写入到磁盘中,因此速度较快。
fsync 是实际的写盘操作,占用磁盘的 IOPS。
write 和 fsync 的写入时机,是由sync_binlog 控制的:
在出现 IO 瓶颈的情况下,可以考虑将 sync_binlog 设置成一个大的值。比较常见的是将 N设置为 100~1000。但是存在的风险是,当主机异常重启时会丢失 N 个最近提交的事务 binlog。
前面介绍过了 redo log 的写入首先会写入 redo log cache,其详细的状态如下所示:
redo log 对应上面的 3 种状态分别是:
InnoDB 有一个后台线程,每个 1 秒钟 就会将 redo log buffer 中的日志,调用 write 写入到 文件系统的 page cache 中,然后再调用 fsync 持久化到磁盘中。redo log buffer 是共享的,因此一些正在执行中的事务的 redo log 也有可能被持久化到磁盘中。
通常我们说的 MySQL 的 “双1” 操作,指的是 sync_binlog = 1 AND innodb_flush_log_at_trx_commit = 1 。innodb_flush_log_at_trx_commit 设置成 1 表示 redo log 在 prepare 阶段就需要持久化一次,那么 “双1” 配置 每个事务提交的时候都会刷盘 2 次,一次是 binlog,一次是 redo log。
为了控制 redo log 的写入策略,innodb_flush_log_at_trx_commit 会有下面 3 中取值:
redo log 实际的触发 fsync 操作写盘包含以下几个场景:1、后台每隔 1 秒钟的线程轮询;2、innodb_flush_log_at_trx_commit 设置成 1 时,事务提交时触发;3、innodb_log_buffer_size 是设置 redo log 大小的参数,当 redo log buffer 达到 innodb_log_buffer_size / 2 时,也会触发一次 fsync。
MySQL 为了优化磁盘持久化的开销,会有一个 组提交(group commit)的机制。首先我们介绍一下 日志逻辑序列号(log sequence number,LSN),它是用来对应每个 redo log 写入点的递增序列号。每次写入长度为 length 的 redo log,LSN 就会加上 length。
下面是 3 个并发事务(trx1、trx2、trx3)在 prepare 阶段都写完了 redo log buffer,然后组提交持久化的过程。其中 3 个事务对应的 LSN 分别是:50、120、160。
从上面描述可以看出,一次组提交里面的组员越多,节约磁盘 IOPS 的效果越好。在并发场景下,一个事务写完 redo log 之后,fsnyc 越晚调用,组员可能越多,其节约IOPS 的效果越好。binlog 和 redo log 的执行过程简图如下所示:
上面 binlog 的写入是 1 个步骤,事实上 binlog 的写入也是分成 2 步的:
从上图的流程可以看出,binlog 的写盘也可以组提交了。当上面执行 binlog: fsync 时,可以将需要写盘的 binlog 一起写入(事务完整 binlog),这样也可以减少一部分 IOPS 的开销。
通常情况下 redo log prepare: fsync 阶段执行的时间较短,此时可能binlog 的组提交可能没有 redo log 的组提交效果那么好。此时可以通过下面 2 个参数来提升 binlog 组提交的效率:
binlog_group_commit_sync_delay:表示延迟多少微秒后,再执行 fsync;
binlog_group_commit_sync_no_delay_count:表示累计多少次后,在调用 fsync;
上面 2 个参数是 或 的关系,满足其中一个就可以触发 fsync。
WAL 机制可以减少磁盘的写入次数,主要得益于下面 2 个方面:
当MySQL 出现了 IO 上面的性能问题,可以考虑下面的优化策略。
设置 binlog_group_commit_sync_delay 和 binlog_group_commit_sync_no_delay_count。可以使用故意等待来减少,binlog 的写盘次数,没有数据丢失的风险,但是会有客户端响应变慢的风险。
设置 sync_binlog 设置为 100~1000 之间的某个值。这样做存在的风险是可能造成 binlog 丢失。
设置 innodb_flush_log_at_trx_commit = 2,可能会丢数据。
参考:《极客时间:MySQL实战》、《高性能MySQL》
链接:http://moguhu.com/article/detail?articleId=127