只要binlog和redo log保证持久化到磁盘,就能保证MySQL异常重启后就能够恢复数据。
binlog的写入机制
事务执行的时候,先把日志写到binlog cache,事务提交的时候,再把binlog cache写入到binlog文件中。
一个事务的binlog是不能拆开的,所以无论这个事务多大都需要确保一次性写入。这就涉及到了binlog cache的保存问题。
系统为每个线程都分配了一块内存作为binlog cache,大小由binlog_cache_size控制,超过了这个大小,就需要保存到磁盘。
事务提交的时候,执行器将binlog cache中的内容写入binlog,然后清空binlog cache。每个线程都有自己的binlog cache,但是共用一份binlog。
一般的写入都是调用write方法,写入文件系统的page cache,执行fsync才是真正写入磁盘。write和fsync的时机,是由参数 ==sync_binlog ==控制的:
- sync_binlog=0 的时候,表示每次提交事务都只 write,不 fsync;
- ==sync_binlog=1 的时候,表示每次提交事务都会执行 fsync==;
- sync_binlog=N(N>1) 的时候,表示每次提交事务都 write,但累积 N 个事务后才 fsync。
redo log写入机制
事务在执行过程中,生成的 redo log 是要先写到 redo log buffer 的。 日志写到 redo log buffer 是很快的,wirte 到 page cache 也差不多,但是持久化到磁盘的速度就慢多了。
为了控制 redo log 的写入策略,InnoDB 提供了 innodb_flush_log_at_trx_commit 参数,它有三种可能取值:
- 设置为 0 的时候,表示每次事务提交时都只是把 redo log 留在 redo log buffer 中 ;
- 设置为 1 的时候,表示每次事务提交时都将 redo log 直接持久化到磁盘;
- 设置为 2 的时候,表示每次事务提交时都只是把 redo log 写到 page cache。
InnoDB 有一个后台线程,每隔 1 秒,就会把 redo log buffer 中的日志,调用 write 写到文件系统的 page cache,然后调用 fsync 持久化到磁盘。
通常我们说MySQL 的“双 1”配置,指的就是 sync_binlog 和 innodb_flush_log_at_trx_commit 都设置成 1。也就是说,一个事务完整提交前,需要等待两次刷盘,一次是 redo log(prepare 阶段),一次是 binlog。
组提交机制
redo log buffer是所有线程公用的,当一个事务的提交的时候redo log buffer写入到磁盘的时候会将其他事务的redo log一并写入到磁盘的。
这里,我需要先和你介绍日志逻辑序列号(log sequence number,LSN)的概念。LSN 是单调递增的,用来对应 redo log 的一个个写入点。每次写入长度为 length 的 redo log, LSN 的值就会加上 length。LSN 也会写到 InnoDB 的数据页中,来确保数据页不会被多次执行重复的 redo log。
现在有三个并发事务在prepare阶段,写完redo log buffer,要持久化到磁盘的过程

- trx1 是第一个到达的,会被选为这组的 leader;
- 等 trx1 要开始写盘的时候,这个组里面已经有了三个事务,这时候 LSN 也变成了 160;
- trx1 去写盘的时候,带的就是 LSN=160,因此等 trx1 返回时,所有 LSN 小于等于 160 的 redo log,都已经被持久化到磁盘;
- 这时候 trx2 和 trx3 就可以直接返回了。 所以,一次组提交里面,组员越多,节约磁盘 IOPS 的效果越好。
所以两阶段提交,将具体过程细分为了这样
让redo log和bin log的write与 fsync互相交叉,让redo log和bin log都能够进行组提交。不过通常情况下第 3 步执行得会很快,所以 binlog 的 write 和 fsync 间的间隔时间短,导致能集合到一起持久化的 binlog 比较少,因此 binlog 的组提交的效果通常不如 redo log 的效果那么好。
通过参数binlog_group_commit_sync_delay 和 binlog_group_commit_sync_no_delay_count 可以提高binlog组提交的效果:
- binlog_group_commit_sync_delay 参数,表示延迟多少微秒后才调用fsync;
- binlog_group_commit_sync_no_delay_count 参数,表示累积多少次以后才调用 fsync。 两者满足一个条件就会执行。
WAL机制每次提交事务都要写redo log和binlog,但是得益于两点可以减少磁盘写:
- redo log 和 binlog 都是顺序写,磁盘的顺序写比随机写速度要快;
- 组提交机制,可以大幅度降低磁盘的 IOPS 消耗。
MySQL出现IO性能瓶颈的处理方法:
- 设置 binlog_group_commit_sync_delay 和 binlog_group_commit_sync_no_delay_count 参数,减少 binlog 的写盘次数。这个方法是基于“额外的故意等待”来实现的,因此可能会增加语句的响应时间,但没有丢失数据的风险。
- 将 sync_binlog 设置为大于 1 的值(比较常见是 100~1000)。这样做的风险是,主机掉电时会丢 binlog 日志。
- 将 innodb_flush_log_at_trx_commit 设置为 2。这样做的风险是,主机掉电的时候会丢数据。