MYSQL45讲学习笔记

23 | MySQL是怎么保证数据不丢的?

之前我们学习了MYSQL的WAL技术,以及 redo log 和 binlog , redo log 保证 crash-safe 功能,binlog 保证数据库可以恢复到某一时刻的状态,那么MYSQL怎样可以保证数据不丢失?

答案就是:只要 redo log 和 binlog 不丢失,就可以保证 MYSQL 数据不丢失。

binlog 写入机制

binlog 的写入逻辑比较简单,先将 binlog 日志写入 biglog cache,在事务提交时,再将 biglog cache 写入到 binlog 文件中。

binlog 的写入是不能被打断的。因为如果 binlog 的写入被打断了,那么会影响逻辑的一致性,可以参考 20 | 幻读是什么,幻读有什么问题?中没有锁,导致 binlog 日志逻辑不一致的场景。

因为binlog 的写入不能被打断,所以不论事务有多大,都必须一次性写入,这就涉及到 binlog cache 的保存问题。

系统给每个线程分配了一个 binlog cache ,参数 binlog_cache_size 控制 binlog cache 的大小,超过这个值就要暂存到磁盘中。

在事务提交时,执行器把 binlog cache 中的数据写入到 binlog 中,并且清空 binlog cache。状态如图所示:

 每个线程有自己的一块 binlog cache,但是共用一份 binlog 文件。

图中的write 表示将 binlog cache 写入到文件系统的 page cache 中,速度较快。

fsync 表示将数据写入到磁盘中,通常我们认为 fsync 才会占用 IOPS。

write 和 fsync 写入的时机由 sync_binlog 参数来控制:

为0时,表示事务提交时只 write;

为1时,表示事务提交时 write 并 fsync;

为N时,表示事务提交时都 write,但是积攒N个事务才会 fsync。

如果设置为0,如果主机异常重启,那么会丢失日志,并且丢失量不可控,所以不建议设置。

在系统遇到IO瓶颈时,可以考虑将其设置为一个较大的值,可以提升性能,但是这样做的风险是,主机异常重启时会丢失N个 binlog 日志。

redo log 写入机制

之前我们学习过 redo log buffer ,知道redo log 是先写入到 redo log buffer,再写入到文件中。

redo log 的状态有三种:

1. 存在于 redo log buffer 中,物理上是MYSQL中的一块内存,对应图中红色部分。

2. 存在于文件系统的 page cache 中,对应图中黄色部分。

3. 存在于磁盘中,对应图中绿色部分。

redo log 的写入过程:

由 redo log buffer write 到文件系统中的 page cache 中,之后 fsync 持久化到磁盘中。

redo log 的写入时机有很多影响因素,参数 innodb_flush_log_at_trx_commit 以及 innodb_log_buffer_size 等都会影响 redo log 写入的时机。

我们先来谈谈 innodb_flush_log_at_trx_commit 这个参数:

值为0时,表示事务提交时,只将数据保存到 redo log buffer 中。

值为1时,表示事务提交时,将数据 write ,并且 fsync 到磁盘中。

值为2时,表示事务提交时,只将数据 write 到文件系统的 page cache 中。

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

那么有没有可能事务还没有提交,但是事务的 redo log 已经持久化到磁盘中了?

答案是有的。

1. 一种是, redo log buffer 占用的内存达到 innodb_log_buffer_size 的一半,那么后台线程会主动刷盘。 这里需要注意,由于事务并没有提交,所以只会 write 到文件系统的 page cache 中,并不会持久化到磁盘中。

2. 另一种是,并行的事务提交了,顺带将这个事务的 redo log buffer 持久化到磁盘中。

之前我们学习过事务的提交顺序图,redo log 先 prepare,再写 binlog,最后再把 redo log commit。

我们常说的“双1配置”,就是将 sync_binlog 和 innodb_flush_log_at_trx_commit 都设置为1,这样可以保证事务提交时 binlog 和 redo log 持久化到磁盘。

“双1配置”意味着事务持久化到磁盘需要两次刷盘,一次是 prepare(redo log),一次是commit(binlog)。

那么这样的话,如果说一个系统的TPS 是两万,那么实际需要四万次IO,但是磁盘能力就是两万,这是怎么做到的呢?

这就要收到组提交(group commit)机制了。

这里我们首先需要了解下 日志逻辑序列号(log sequeence number,LSN) 这个概念,LSN 是单调递增的,用来对应 redo log 的一个个写入点,每次写入长度为 length 的 redo log,LSN就加length。

LSN也会被用于 innodb 的数据页中,用来确保数据页不会被多次执行重复的 redo log。

如图所示,是三个并发事务(trx1,trx2,trx3)在prepare 阶段,redo log 写入磁盘的过程,trx分别为 50,120,160

从图中可以看到:

1.trx1是最先达到的,被选为这组的leader。

2.等 trx1 写盘的时候,这个组里面已经有了三个事务,此时LSN变成了160。

3.trx1 持久化的时候,带的就是 160 的LSN,所有LSN小于等于 160 的 redo log,都被持久化到磁盘。

4.这时候 trx2 和 trx3 就可以直接返回了。

所以,一组提交里面,组员越多,节约磁盘IOPS效果越好,但如果是单线程,那就只能一个事务对应一个持久化操作了。

在并发更新场景下,第一个事务写完 redo log 以后,fsync 越慢调用,组员越多,节约IOPS的效果越好。

为了让一次 fsync 带的组员更多,MYSQL 有一个很有趣的优化:拖时间。

之前我们学习过事务提交时写入 redo log 和 binlog 的时序图,

之前的图中,写 binlog 是一个动作,但是实际上,写 binlog 是分两步的:

1. 将 binlog 从 binlog cache write 到文件系统的 page cache 中,

2. 调用 fsync 函数持久化。

MYSQL为了让组提交的效果更好,将 redo log  fsync 的时间放在了步骤1之后,

 这样一来,binlog 也变成了两阶段提交,在图中第四部 binlog fsync 时,如果已经有多个事务 binlog write 了,那么也是可以一起持久化的。

不过通常情况第三部执行的很快,所以能一起持久化的 binlog 比较少,所以 binlog 组提交的效果一般不如 redo log 组提交那么好。

如果我们想要提升 binlog 组提交的效果,可以设置参数 binlog_group_commit_sync_delay 和参数 binlog_group_commit_sync_no_delay_count.

1.binlog_group_commit_sync_delay,表示 binlog write 之后延迟多少秒才 fsync。

2.binlog_group_commit_sync_no_delay_count,表示write积累了多少次之后才 fsync。

这两个参数之间是或的关系,如果 binlog_group_commit_sync_delay 参数设置为0之后,binlog_group_commit_sync_no_delay_count 参数的设置也就无效了。

WAL机制的好处是 减少磁盘写,得益于两个方面:

1.binlog 和 redo log 都是顺序写,顺序写比随机写的速度 快很多。

2.组提交机制可以降低系统的 IOPS。

当我们在遇到IO性能瓶颈的时候,我们可以考虑以下几个方案:

1.将 sync_binlog 设置为N,这样做的风险是,主机掉电时可能会丢失N个事务。

2.将 innodb_flush_log_at_trx_commit 设置为2,同样,主机掉电时可能会丢失数据。

3.设置 binlog_group_commit_sync_delay 和 binlog_group_commit_sync_no_delay_count参数,减少 binlog 的写盘次数,这个方法是通过“延长等待时间”来实现的,可能会造成语句响应时间延长,但是没有丢失数据的风险。

不建议把innodb_flush_log_at_trx_commit 设置为0,因为这样主机 异常重启也会丢数据,而设置为2,主机异常重启不会丢数据,只有主机掉电重启时才会丢数据,并且两个参数的性能差不多。

小结

本节我们学习了 binlog 和 redo log 的写入机制,以及一些参数,例如:sync_binlog ,innodb_flush_log_at_trx_commit  , innodb_flush_log_size  ,binlog_group_commit_sync_delay 和 binlog_group_commit_sync_no_delay_count 。

还有LSN的概念以及组提交机制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值