只要redo log和binlog保证持久化到磁盘,就能确保MySQL异常重启后,数据可以恢复。我们接下来来看看MySQL写入binlog和redo log的流程。
binlog的写入机制
binlog的写入逻辑比较简单:事务执行过程中,先把日志写到binlog cache,这个操作会调用write方法,并没有把数据持久化到磁盘,速度比较快;事务提交的时候,再把binlog cache写到binlog文件中,这步会调用fsync将数据持久化到磁盘,速度比较慢。
write和fsync的时机,是由参数sync_binlog
控制的:
sync_binlog=0
的时候,表示每次提交事务都只write,不fsync。sync_binlog=1
的时候,表示每次提交事务都会执行fsync。sync_binlog=N(N>1)
的时候,表示每次提交事务都write,但累积N个事务后才fsync。
在出现IO瓶颈的场景中,将sync_binlog
设置成一个比较大的值,可以提升性能。实际业务场景中,通常设置为100~1000中的某个数值。这样做对应的风险是:如果主机发生一场重启,会丢失最近N个事务的binlog。
redo log的写入机制
和binlog一样,redo log也是先写到redo log buffer,然后再合适的时间持久化到磁盘。
InnoDB提供了配置参数innodb_flush_log_at_trx_commit
。
- 设置为0的时候,表示每次事务提交时都只是把redo log留在redo log buffer中。
- 设置为1的时候,表示每次事务提交时都将redo log直接持久化到磁盘。
- 设置为2的时候,表示每次事务提交时都只是把redo log写到page cache。
InnoDB有一个后台线程,每隔1s就会把redo log buffer中的日志,调用write写到文件系统的page cache,然后调用fsync持久化到磁盘。
通常我们说MySQL的“双1”配置,指的就是sync_binlog
和innodb_flush_log_at_trx_commit
都设置成1.也就是说,一个事务完整提交前,需要等待两次刷盘,一次是redo log(prepare阶段),一次是binlog。
// redo log crash-safe
innodb_flush_log_at_trx_commit 设置为1 表示每次事务的redo log都直接持久化到磁盘
sync_binlog 设置为1,表示每次事物的binlog都持久化到磁盘
我们可以看到,WAL机制是减少磁盘写,可是每次提交事务都要写redo log和binlog,磁盘读写次数并没有减少,但是性能有提高,WAL主要得益于两个方面:
- 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,这样做的风险是,主机掉电的时候会丢数据。
***什么时候会把线上生产库设置成“非双1”***:
- 业务高峰期
- 备库延迟,为了让备库尽快赶上主库
- 批量倒入数据的时候
redo log是物理日志,记录的是“在某个数据叶上做了什么修改”,只有它自己才可以使用。binlog是逻辑日志,记录的是语句的原始逻辑。