binlog和redolog如何写入磁盘
# binlog和redolog如何写入磁盘
# binlog写入机制
binlog cache:事务会把日志写到内存里的binlog cache,作为缓冲区。每个线程单独持有一份cache内存空间。
binlog_cache_size:单个线程内binlog cache占用内存的大小。如果事务的日志超过这个阈值会暂存到磁盘。
每个线程事务都会有自己一块binlog cache,而所有线程共用同一份binlog file。

整个binlog写磁盘分为两个过程:
- write:指的是每个线程将自己的日志写入到文件系统的page cache,并没有把数据持久化到磁盘,因此这一步速度比较快,
- fsync:真正将数据同步持久化到磁盘上。这一步占用磁盘的IOPS。
具体这两个步骤的执行时机,是通过sync_binlog参数控制:
- sync_binlog=0:表示每次事务提交只write,不会fsync;
- sync_binlog=1:表示每次事务提交都会执行write和fsync
- sync_binlog=N(N>1):每次事务提交都会write,只有当binlog files积累了N个事务的日志后,才会执行fsync持久化到磁盘。
💡实际场景中,一般sync_binlog设置为100~1000中的某个值,因为fsync是比较耗时吃性能的,设置日志分批持久化的阈值可以有效提高整个数据库的性能。当然这种方法如果数据库异常重启或者宕机之后,这N个事务的日志不能恢复。
# redolog写入机制
redolog buffer:在MySQL服务端,所有线程共用一个内存的redolog buffer,不同事务的日志都会写到这个区域。由于两阶段提交机制,buffer中的日志生成后只要没有持久化到磁盘,即使丢失了也没有关系。
与binlog类似,整个redolog日志可能存在三种状态:
- 存在MySQL内存的redolog buffer中
- write写到磁盘文件系统的page cache(本质是文件系统向内核申请的一块内存),但是还没有fsync持久化。
- fsync持久化到磁盘hard dist。这一步骤最影响数据库性能,写到磁盘速度比较慢。
具体redolog日志的写入策略,通过innodb_flush_log_at_trx_commit参数控制:
- innodb_flush_log_at_trx_commit=0:每次事务提交都只是将redolog留在内存redolog buffer
- innodb_flush_log_at_trx_commit=1:每次事务提交都将日志fsync持久化到磁盘中
- innodb_flush_log_at_trx_commit=2:每次事务提交只把redolog写到page cache(主机断电会丢失,但是MySQL重启不会丢失)。
上面讲了内存中的redolog可以丢失,只要有两阶段提交协议和事务保证一致性即可。实际上即使当前事务没有提交,在下面这几种场景下,内存redolog buffer中的日志也可能已经持久化到磁盘当中。
- InnoDB有一个后台线程,每隔一秒的轮询机制就会将redolog buffer中的日志先write再fsync持久化到磁盘。
- 内存redolog buffer的日志文件大小如果达到innodb_log_buffer_size的一半,那么后台线程会主动写到磁盘的page cache。
- 其它并行事务B在进行提交后,即使当前事务A还没执行完,也会连带将整个redolog buffer日志一并提交。
注意这里如果innodb_flush_log_at_trx_commit=1,那么一阶段redolog更改为prepare时,就需要持久化一次,因为这里存在一种崩溃恢复的情况,需要根据redolog和binlog状态和完整性来恢复。而最后commit后redolog只需要write到page cache就足够了。
“双1配置”:sync_binlog和innodb_flush_log_at_trx_commit都设置为1,一个事务从开始到提交等待两次刷盘:①redolog(prepare)②binlog
# 组提交机制
日志逻辑序列号LSN (log sequence number):对应redolog的写入点,在LSN写入length长度的日志文件后,下一个LSN就会更新为LSN+length。注意leader在开始写盘之前,每进来一个事务LSN都会增加。

- trx1第一个到达,会作为本组的leader
- trx1写日志之前,组里总共进来了三个事务,LSN为160
- trx1进行写盘时带着LSN=160进行持久化,也就是上面的场景3,它会连带trx2和trx3的日志一并持久化
- 整个组内所有LSN小于160的redolog最终都会被持久化。
并发场景下,一个组内的事务越多,准备阶段redolog的fsync越晚,那么节约磁盘IOPS效果越好。
# MySQL针对组提交的延迟持久化
两阶段提交①写入redolog(prepare阶段)②写binlog③提交事务redolog提交状态。根据上述两个日志文件的写盘流程,并且经过MySQL优化流程后,原先的两阶段提交可以进一步细化为如下状态:

- 将处于prepare状态的redolog的fsync刷盘流程延迟放到第三步进行,从而使组提交节省更多的IOPS。
- 步骤4binlog进行刷盘时也可以进行组提交(因为binlog files也是所有事务共享的),但步骤3执行速度比较快,组提交效果通常不如redolog
- 要想提升binlog组提交效果,可以通过设置binlog_group_commit_sync_delay和binlog_group_commit_sync_no_delay_count实现
binlog_group_commit_sync_delay:表示延后多少微妙后才调用fsync
binlog_group_commit_sync_no_delay_count:表示累计多少次后才调用fsync。N个事务后提交才返回给客户端,也就是说客户端只要能拿到,那么一定是持久化过的。
# 磁盘IO瓶颈问题解决方案
方案一、设置binlog_group_commit_sync_delay和binlog_group_commit_sync_no_delay_count两个参数,延长刷盘的时机,减少磁盘IO次数。
缺点:可能会延长SQL语句的i响应时间
方案二、设置sync_binlog改为大于1,分组刷盘提交
缺点:数据库宕机可能会导致内存中N个事务日志的丢失
方案三、设置innodb_flush_log_at_trx_commit为0,将redolog写到文件系统的page cache
缺点:主机断电丢失redolog
# “非双1”设置的场景
显然在“双1”设置下,事务每次提交都会马上直接持久化binlog和redolog,高并发场景下磁盘读写次数增加,消耗IOPS,从而导致增加数据库的写盘压力。因此在以下场景,需要设置成“非双1”配置:innodb_flush_logs_at_trx_commit=2、sync_binlog=1000
- 业务高峰期。如果有可以预知的业务高峰期,DBA需要将主库设置成”非双1“
- 备库延迟。主从模式下,如果备库设置双1,会导致主库还没有持久化,备库就已经完成持久化并将日志更新成最新了。当备库追上主库后再改回来。
- 用备份恢复主库的副本,应用binlog的过程。
- 批量导入数据的时候。