天天看点

【mysql45讲】redo log & binlog1.WAL2.redo log介绍3.binlog介绍4.redo log和binlog的区别5.两阶段提交(使用redo log和binlog)6.MySQL 的“双 1”配置7.组提交机制8.如果MySQL 现在出现了性能瓶颈,而且瓶颈在 IO 上,可以通过哪些方法来提升性能呢?

1.WAL

WAL 的全称是 Write-Ahead Logging,它的关键点就是先写日志(写日志也是写磁盘,不过是顺序写,所以快),再写磁盘。

只要 redo log 和 binlog 保证持久化到磁盘,就能确保 MySQL 异常重启后,数据可以恢复。

WAL 机制主要得益于两个方面:

redo log 和 binlog 都是顺序写,磁盘的顺序写比随机写速度要快;

组提交机制,可以大幅度降低磁盘的 IOPS 消耗。

2.redo log介绍

2.1 redo log循环写

redo log属于InnoDB日志。

redo log 是固定大小的,比如可以配置为一组 4 个文件,每个文件的大小是 1GB,那么总共就可以记录 4GB 的操作。从头开始写,写到末尾就又回到开头循环写。

示例如图:

【mysql45讲】redo log & binlog1.WAL2.redo log介绍3.binlog介绍4.redo log和binlog的区别5.两阶段提交(使用redo log和binlog)6.MySQL 的“双 1”配置7.组提交机制8.如果MySQL 现在出现了性能瓶颈,而且瓶颈在 IO 上,可以通过哪些方法来提升性能呢?

write pos 是当前记录的位置,一边写一边后移,写到第 3 号文件末尾后就回到 0 号文件开头。checkpoint 是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。write pos 和 checkpoint 之间的是“粉板”上还空着的部分,可以用来记录新的操作。如果 write pos 追上 checkpoint,表示“粉板”满了,这时候不能再执行新的更新,得停下来先擦掉一些记录,把 checkpoint 推进一下。

2.2 crash-safe

即使数据库异常重启,之前的记录也不会丢失,这就是redo log有crash-safe能力。

innodb_flush_log_at_trx_commit 这个参数设置成 1 的时候,表示每次事务的 redo log都直接持久化到磁盘。这个参数我建议你设置成 1,这样可以保证 MySQL 异常重启之后数据不丢失。

2.3 redo log的写入机制

redo log可能存在的三种状态如下:

1.存在 redo log buffer 中,物理上是在 MySQL 进程内存中;

2.写到磁盘 (write),但是没有持久化(fsync),物理上是在文件系统的 page cache 里;

3.持久化到磁盘,对应的是 hard disk。

日志写到 redo log buffer 是很快的,wirte 到 page cache 也差不多,但是持久化到磁盘的速度就慢多了。

为了控制 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。

没有提交事务的redo log被写入到磁盘的三种情况:

1.InnoDB 有一个后台线程,每隔 1 秒,就会把 redo log buffer 中的日志,调用 write 写到文件系统的 page cache,然后调用 fsync 持久化到磁盘。

2.redo log buffer 占用的空间即将达到 innodb_log_buffer_size 一半的时候,后台线程会主动写盘。注意,由于这个事务并没有提交,所以这个写盘动作只是 write,而没有调用 fsync,也就是只留在了文件系统的 page cache。

3.并行的事务提交的时候,顺带将这个事务的 redo log buffer 持久化到磁盘。假设一个事务 A 执行到一半,已经写了一些 redo log 到 buffer 中,这时候有另外一个线程的事务 B 提交,如果 innodb_flush_log_at_trx_commit 设置的是 1,那么按照这个参数的逻辑,事务 B 要把 redo log buffer 里的日志全部持久化到磁盘。这时候,就会带上事务 A 在 redo log buffer 里的日志一起持久化到磁盘。

2.4 redo log的目的

确保事务的持久性。防止在发生故障的时间点,尚有脏页未写入磁盘,在重启mysql服务的时候,根据redo log进行重做,从而达到事务的持久性这一特性。

3.binlog介绍

3.1 binlog

binlog属于Server层的日志(归档日志)。

它可以追加写入,不会覆盖之前的。

只依靠binlog是没有crash-safe能力的。

sync_binlog 这个参数设置成 1 的时候,表示每次事务的 binlog 都持久化到磁盘。这个参数我也建议你设置成 1,这样可以保证 MySQL 异常重启之后 binlog 不丢失。

3.2 binlog的写入机制

事务执行过程中,先把日志写到 binlog cache,事务提交的时候,再把 binlog cache 写到 binlog 文件中。

一个事务的 binlog 是不能被打断的,一个事务的binlog必须连续写。这就涉及到了 binlog cache 的保存问题。系统给 binlog cache 分配了一片内存,每个线程一个,

参数 binlog_cache_size 用于控制单个线程内 binlog cache 所占内存的大小。如果超过了这个参数规定的大小,就要暂存到磁盘。事务提交的时候,执行器把 binlog cache 里的完整事务写入到 binlog 中,并清空 binlog cache。

【mysql45讲】redo log & binlog1.WAL2.redo log介绍3.binlog介绍4.redo log和binlog的区别5.两阶段提交(使用redo log和binlog)6.MySQL 的“双 1”配置7.组提交机制8.如果MySQL 现在出现了性能瓶颈,而且瓶颈在 IO 上,可以通过哪些方法来提升性能呢?

可以看到,每个线程有自己 binlog cache,但是共用同一份 binlog 文件。

图中的 write,指的就是指把日志写入到文件系统的 page cache,并没有把数据持久化到磁盘,所以速度比较快。

图中的 fsync,才是将数据持久化到磁盘的操作。一般情况下,我们认为 fsync 才占磁盘的 IOPS。

write 和 fsync 的时机,是由参数 sync_binlog 控制的:

sync_binlog=0 的时候,表示每次提交事务都只 write,不 fsync;

sync_binlog=1 的时候,表示每次提交事务都会执行 fsync;

sync_binlog=N(N>1) 的时候,表示每次提交事务都 write,但累积 N 个事务后才 fsync。(如果主机发生异常重启,会丢失最近 N 个事务的 binlog 日志。)

因此,在出现 IO 瓶颈的场景里,将 sync_binlog 设置成一个比较大的值,可以提升性能。在实际的业务场景中,考虑到丢失日志量的可控性,一般不建议将这个参数设成 0,比较常见的是将其设置为 100~1000 中的某个数值。

3.3 binlog的三种模式区别

1.Statement

每一条修改数据的 sql 都会记录到 master 的 binlog 中,slave 在复制的时候,sql 进程会解析成和原来在 master 端执行时的相同的 sql 再执行。

2.Row

日志中会记录每一行数据被修改的形式,然后在 slave 端再对相同的数据进行修改。row 模式只记录要修改的数据,只有 value,不会有 sql 多表关联的情况。

3.Mixed

MySQL 会根据执行的每一条具体的 SQL 语句来区分对待记录的日志形式,也就是在 statement 和 row 之间选择一种。

3.4 binlog的目的

两个作用:备份和复制。

binlog 用于主从复制中,从库利用主库上的 binlog 进行重播,实现主从同步。用于数据库的基于时间点、位点等的还原操作。

4.redo log和binlog的区别

1.redo log是InnoDB引擎特有的,而binlog属于Server层所有引擎都能用。

2.redo log是物理日志,记录的是“在某个数据页上实现了什么修改”。binlog是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ”。

3.redo log循环写,空间固定会用完。binlog追加写,文件写到一定大小会切换下一个,不会覆盖以前的日志。

5.两阶段提交(使用redo log和binlog)

5.1 两阶段提交介绍

比如执行下面这条语句

mysql> update T set c=c+1 where ID=2;

过程如图

【mysql45讲】redo log & binlog1.WAL2.redo log介绍3.binlog介绍4.redo log和binlog的区别5.两阶段提交(使用redo log和binlog)6.MySQL 的“双 1”配置7.组提交机制8.如果MySQL 现在出现了性能瓶颈,而且瓶颈在 IO 上,可以通过哪些方法来提升性能呢?

写入redo log分为:prepare和commit两个步骤

两阶段提交过程:写入redo log(处于prepare状态)->写binlog到磁盘->redo log commit

两阶段提交目的:让两份日志逻辑一致

5.2 两阶段提交如何保证数据库异常重启时数据完整性

5.2.1 举例

【mysql45讲】redo log & binlog1.WAL2.redo log介绍3.binlog介绍4.redo log和binlog的区别5.两阶段提交(使用redo log和binlog)6.MySQL 的“双 1”配置7.组提交机制8.如果MySQL 现在出现了性能瓶颈,而且瓶颈在 IO 上,可以通过哪些方法来提升性能呢?

A时刻异常重启:由于redo log未提交,binlog也没写,这个事务会回滚。

B时刻异常重启:redo log写完未提交,binlog已写。对应以下的2(a)情况,崩溃恢复时事务会提交。

崩溃恢复时的判断规则:

如果 redo log 里面的事务是完整的,也就是已经有了 commit 标识,则直接提交;

如果 redo log 里面的事务只有完整的 prepare,则判断对应的事务 binlog 是否存在并完整:

a. 如果是,则提交事务;b. 否则,回滚事务。

5.2.2 其他问题

1.怎样知道binlog完整性

一个事务的 binlog 是有完整格式的:statement 格式的 binlog,最后会有 COMMIT;row 格式的 binlog,最后会有一个 XID event。

另外,在 MySQL 5.6.2 版本以后,还引入了 binlog-checksum 参数,用来验证 binlog 内容的正确性。对于

binlog 日志由于磁盘原因,可能会在日志中间出错的情况,MySQL 可以通过校验 checksum 的结果来发现。所以,MySQL

还是有办法验证事务 binlog 的完整性的。

2.怎么关联redo log和binlog

redo log和binlog有个共同的字段XID。崩溃恢复的时候会按顺序扫描redo log:

碰到有prepare,commit的redo log,直接提交;

碰到有prepare,没commit的redo log,就拿着这个XID去找binlog。

3.只有binlog为什么不支持数据恢复

【mysql45讲】redo log & binlog1.WAL2.redo log介绍3.binlog介绍4.redo log和binlog的区别5.两阶段提交(使用redo log和binlog)6.MySQL 的“双 1”配置7.组提交机制8.如果MySQL 现在出现了性能瓶颈,而且瓶颈在 IO 上,可以通过哪些方法来提升性能呢?
【mysql45讲】redo log & binlog1.WAL2.redo log介绍3.binlog介绍4.redo log和binlog的区别5.两阶段提交(使用redo log和binlog)6.MySQL 的“双 1”配置7.组提交机制8.如果MySQL 现在出现了性能瓶颈,而且瓶颈在 IO 上,可以通过哪些方法来提升性能呢?

6.MySQL 的“双 1”配置

sync_binlog 和 innodb_flush_log_at_trx_commit 都设置成 1。也就是说,一个事务完整提交前,需要等待两次刷盘,一次是 redo log(prepare 阶段),一次是 binlog。

7.组提交机制

7.1 日志逻辑序列号LSN

LSN 是单调递增的,用来对应 redo log 的一个个写入点。每次写入长度为 length 的 redo log, LSN 的值就会加上 length。LSN 也会写到 InnoDB 的数据页中,来确保数据页不会被多次执行重复的 redo log。

7.2 redo log组提交

举例如图:三个并发事务 (trx1, trx2, trx3) 在 prepare 阶段,都写完 redo log buffer,持久化到磁盘的过程,对应的 LSN 分别是 50、120 和 160。

【mysql45讲】redo log & binlog1.WAL2.redo log介绍3.binlog介绍4.redo log和binlog的区别5.两阶段提交(使用redo log和binlog)6.MySQL 的“双 1”配置7.组提交机制8.如果MySQL 现在出现了性能瓶颈,而且瓶颈在 IO 上,可以通过哪些方法来提升性能呢?
【mysql45讲】redo log & binlog1.WAL2.redo log介绍3.binlog介绍4.redo log和binlog的区别5.两阶段提交(使用redo log和binlog)6.MySQL 的“双 1”配置7.组提交机制8.如果MySQL 现在出现了性能瓶颈,而且瓶颈在 IO 上,可以通过哪些方法来提升性能呢?

所以,一次组提交里面,组员越多,节约磁盘 IOPS 的效果越好。但如果只有单线程压测,那就只能老老实实地一个事务对应一次持久化操作了。在并发更新场景下,第一个事务写完 redo log buffer 以后,接下来这个 fsync 越晚调用,组员可能越多,节约 IOPS 的效果就越好。

7.3 mysql做的优化,使binlog也可以组提交

写 binlog 是分成两步的:先把 binlog 从 binlog cache 中写到磁盘上的 binlog 文件;调用 fsync 持久化。

MySQL 为了让组提交的效果更好,把 redo log 做 fsync 的时间拖到了binlog write之后。也就是说,图变成了这样:

【mysql45讲】redo log & binlog1.WAL2.redo log介绍3.binlog介绍4.redo log和binlog的区别5.两阶段提交(使用redo log和binlog)6.MySQL 的“双 1”配置7.组提交机制8.如果MySQL 现在出现了性能瓶颈,而且瓶颈在 IO 上,可以通过哪些方法来提升性能呢?

这样,binlog 也可以组提交了。在执行图 中第 4 步把 binlog fsync 到磁盘时,如果有多个事务的 binlog 已经写完了,也是一起持久化的,这样也可以减少 IOPS 的消耗。不过通常情况下第 3 步执行得会很快,所以 binlog 的 write 和 fsync 间的间隔时间短,导致能集合到一起持久化的 binlog 比较少,因此 binlog 的组提交的效果通常不如 redo log 的效果那么好。

【mysql45讲】redo log & binlog1.WAL2.redo log介绍3.binlog介绍4.redo log和binlog的区别5.两阶段提交(使用redo log和binlog)6.MySQL 的“双 1”配置7.组提交机制8.如果MySQL 现在出现了性能瓶颈,而且瓶颈在 IO 上,可以通过哪些方法来提升性能呢?

8.如果MySQL 现在出现了性能瓶颈,而且瓶颈在 IO 上,可以通过哪些方法来提升性能呢?

【mysql45讲】redo log & binlog1.WAL2.redo log介绍3.binlog介绍4.redo log和binlog的区别5.两阶段提交(使用redo log和binlog)6.MySQL 的“双 1”配置7.组提交机制8.如果MySQL 现在出现了性能瓶颈,而且瓶颈在 IO 上,可以通过哪些方法来提升性能呢?