目录
WAL机制告诉我们:只要redo log与binlog保证持久化到磁盘里,就能确保MySQL异常重启后,数据可以恢复。
下面主要记录一下MySQL写入binlog和redo log的流程。
binlog写入机制
1、事务执行过程中,先把日志写到binlog cache,事务提交的时候,再把binlog cache写到binlog文件中。
2、binlog cache,系统为每个线程分配了一片binlog cache内存,参数binlog_cache_size控制单个线程内binlog cache大小。如果超过了这个大小就要暂存磁盘
3、事务提交的时候,执行器把binlog cache里完整的事务写入binlog中。并清空binlog cache
4、每个线程都有自己的binlog cache,共用一份binlog文件
5、write,是把日志写入到文件系统的page cache,内存中,没有持久化到磁盘,所以速度比较快,图中的fsync是将数据持久化到磁盘,占用磁盘的IOPS
关于何时write、fsync是由参数sync_binlog控制的:
1、sync_binlog = 0时,每次提交事务都只write,不fsync;
2、sync_binlog = 1时,每次提交事务都会执行fsync;
3、sync_binlog = N(N>1)时,表示每次提交事务都write,但累积N个事务后才fsync。
sync_binlog控制binlog真正刷盘的频率,对于一个IO非常大的情景,这个数字调大可以提高性能,但是如果容错率非常低的情况下,必须设为1.(sync_binlog设置为N对应的风险是:如果主机发生异常重启,会丢失最近N个事务的binlog日志)
redo log写入机制
事务在执行过程中,生成的redo log是要先写到redo log buffer的。
redo log buffer里面的内容并不需要每次生成后都要持久化到磁盘中。
如果事务执行期间MySQL发生异常重启,那么这部分日志就丢了。由于事务并没有提交,所以这时日志丢了也不会有损失。
事务没提交的时候,redo log buffer部分日志也是有可能被持久化到磁盘中的。
上面三个颜色表征了redo log可能的三种状态:
1、存在redo log buffer中,物理上是在MySQL进程内存中,即红色部分;
2、写到磁盘(write),但是没有持久化(fsync),物理上实在文件系统的page cache里面,即黄色部分;
3、持久化到磁盘,对应的是hard disk,也就是图中的绿色部分;
前两步是写内存,最后一步是磁盘IO,所以要在page cache够大且不影响写入page cache前将redo log 持久化到磁盘 。
为了控制redo log 的写入策略,InnoDB提供了innodb_flush_log_at_trx_commit参数,他有三种可能取值:
1、设置为0,每次事务提交的时候都只是把redo log留在redo log buffer中;
2、设置为1,每次事务提交的时候都只是把redo log直接持久化到磁盘;
3、设置为2,每次事务提交时都只是把redo log写到page cache;
与binlog不同,binlog是每个线程都有一个binlog cache,而redo log是多个线程共用一个redo log buffer。
InnoDB有一个后台线程,每隔1s,就会把redo log buffer中的日志,调用write写到文件系统的page cache,然后调用fsync持久化到磁盘,事务执行过程中的redo log也是直接写在redo log buffer上的,所以,未提交的事务的redolog也可能被持久化到磁盘。
还有两种场景也会导致没有提交的事务的redo log写入到磁盘中:
情形1:
redo log buffer占用的空间即将达到innodb_log_buffer_size一半的时候,后台线程会主动写盘。
(这里只是write,没有fsync)
情形2:
并行的事务提交的时候,顺带将这个事务的redo log buffer持久化到磁盘。
(事务A执行一半,部分redo log到buffer中;事务B提交,且 innodb_flush_log_at_trx_commit ,会把redo log buffer里的log全部持久化到磁盘中)
补充说明
两阶段提交在时序上redo log先prepare 再写binlog,最后再把redo log commit;
innodb_flush_log_at_trx_commit 设置成 1,prepare阶段redo log就已经落盘。所以redo log再commit的时候就不需要fsync了,只会write到文件系统的page cache中就够了。
sync_binlog 和 innodb_flush_log_at_trx_commit都设置为1,即一个事务完整提交前,需要等待两次刷盘,一次是redo log(prepare阶段),一次是binlog。
组提交机制实现大量的TPS
首先介绍日志逻辑序列号(log sequence number,LSN)的概念。LSN是单调递增的,每次写入长度length的redo log,LSN的值就会加上length。
三个并发事务(trx1,trx2,trx3)在prepare阶段,都写完redo buffer,并持久化到磁盘。
对应的LSN为50、120、160.
对应流程:
1、trx1第一个到达,被选为这组的leader;
2、等trx1要开始写盘的时候,这个组里面已经有三个事务,这时候LSN也变成了160;
3、trx1去写盘的时候,LSN=160;trx1返回时,所有LSN<= 160的redo log都被持久化到磁盘中;
4、trx2与trx3直接返回。
总结:
一次组提交中,组员越多,节约磁盘IOPS的效果越好。如果是单线程,就只能一个事务对应一次持久化操作
两阶段提交 | 两阶段提交细化 |
这样保证binlog也可以组提交了。由于step3速度快,所以集合到一起的binlog比较少,所以binlog的组提交效果不如redo log组提交。
提升binlog效果:
--1.binlog_group_commit_sync_delay :b表示延迟多少微秒后才调用fsync;
--2.binlog_group_commit_sync_no_delay_count :表示累积多少次以后才调用fsync;
理解WAL机制
WAL机制是减少磁盘写,可是每次提交事务都要写redo log和binlog ,磁盘读写次数没有变少。
所以WAL机制主要得益于两个方面:
--1、redo log和binlog都是顺序写,磁盘的顺序写比随机写速度要快
--2、组提交机制,可以降低磁盘IOPS消耗
如何提升IO性能瓶颈
1、设置binlog_group_commit_sync_delay
和binlog_group_commit_sync_no_delay_count
参数,减少binlog的写盘次数。这个方法是基于“额外故意等待”来实现的,可能会增加语句的响应时间,但是不会丢失数据
2、将sync_binlog
设置为大于1的值(100~1000)。不过会有主机掉电时丢binlog日志的风险
3、将 innodb_flush_log_at_trx_commit 设置为2。会有主机掉电丢数据的风险