目录


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
《MySQL——redo log 与 binlog 写入机制》_数据库

关于何时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部分日志也是有可能被持久化到磁盘中的。
《MySQL——redo log 与 binlog 写入机制》_数据库_02
上面三个颜色表征了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的效果越好。如果是单线程,就只能一个事务对应一次持久化操作

《MySQL——redo log 与 binlog 写入机制》_文件系统_03两阶段提交


《MySQL——redo log 与 binlog 写入机制》_数据库_04两阶段提交细化


这样保证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。会有主机掉电丢数据的风险