关键字
redo log、数据 flush、磁盘性能、内存淘汰
0.概述和引子
在第二篇文章中说到,redo log 可以将零散读取的磁盘操作暂时用顺序写进行存储,随后在合适的实际将 redo log 写到磁盘的数据页中。但是,当redo log 的这个操作执行在不恰当的时候执行,就会显著降低数据库的性能。
这是为什么呢?让我们来看今天的内容。
1.flush
当内存数据页跟磁盘数据页内容不一致的时候,我们称这个内存页为“脏页”,内存的数据写入到磁盘后,内存和磁盘上的数据页的内容一致,称为“干净页”。
从内存中将数据写入磁盘的过程,也就是内存中的脏页变成干净页的过程,称为 flush。在下面的图中,展示了一个 19 的脏页通过 redo log 和刷磁盘,将 脏页 flush 为 干净页 的过程(请无视一些无关名词,哈哈哈):
12-redo log的flush.jpeg可以预见到,flush 会耗费磁盘的 IO 资源,这势必会对数据库的性能造成影响。那么,在什么情况下会引发数据库的 flush 呢?
2.引发 flush 的场景
- 第一种场景:redo log 写满了,这时候就需要将 redo log 中的数据 flush 掉,以便给之后的日志提供空间。
- 第二种场景:MySQL 持有的系统内存不足,当一个新的查询语句到来的时候,需要使用内存,此时就必须将内存中现有的数据淘汰掉。
- 第三种场景:MySQL 在系统空闲的时候进行 flush。
- 第四种场景:MySQL 正常关闭,要将内存中的 脏页 和 redo log 清空。
那么,我们分析一下这三种情况对性能的影响:
- 第三种和第四种,对数据库的性能影响很小。
- 第一种情况:redo log 满了,要 flush 脏页。这种情况是要尽量避免的,因为出现这种情况,整个系统就不再接受更新,数据库的更新量为 0 。
- 第二种情况:内存满,需要淘汰数据页。
如果淘汰的是干净页,可以直接释放内存;
如果淘汰的是脏页,就需要先进行 flush 。
显然,分析了上面的场景,有两种情况会影响性能:
- redo log 写满,必须等待 flush ,此时数据库性能为 0。
- 内存满了,要淘汰的脏页太多,查询的响应时间变长。
知道了这两点我们就要避免他们的出现,这就要说到 InnoDB 刷脏页的控制策略。
3.InnoDB 刷脏页的控制策略
3.1设置innodb_io_capacity
首先,InnoDB 需要知道主机磁盘的 IO 能力,针对这个 IO 能力,InnoDb 会设置刷 flush 的频率。这个参数是 innodb_io_capacity 。同时你可以用 fio 工具测试磁盘的 IOPS(每秒输入输出的数据量),使用它的语句如下:
fio -filename=$filename -direct=1 -iodepth 1 -thread -rw=randrw -ioengine=psync -bs=16k -size=500M -numjobs=10 -runtime=10 -group_reporting -name=mytest
如果 MySQL 的写入速度很慢,但是数据库主机的 IO 压力并不大,那很可能是你的 innodb_io_capacity 设置过小。
如果面临 redo log 写满 或 内存写满 的情况,InnoDB 就会使用 innodb_io_capacity 设置的值 flush 数据。当然,这是全速刷 flush 的情况,在平常,我们需要控制它的速度。
3.2控制刷脏页的速度
控制这个速度,需要考虑两个因素:内存中的脏页比例,以及 redo log 的写入速度。
具体的内容比较复杂,简单来说,就是如果内存中的脏页比例过高(通过 innodb_max_dirty_pages_pct 参数设置,一般设置为 75%),或 InnoDB 写 redo log 的速度过快,都有可能加速 InnoDB 的 刷脏页速度。
3.3连坐机制
在准备刷一个脏页的时候,如果这个数据页旁边的数据页刚好是脏页,就会把这个“邻居”也带着一起刷掉;而且这个把“邻居”拖下水的逻辑还可以继续蔓延,也就是对于每个邻居数据页,如果跟它相邻的数据页也还是脏页的话,也会被放到一起刷。
在 InnoDB 中,innodb_flush_neighbors 参数就是用来控制这个行为的,值为 1 的时候会有上述的“连坐”机制,值为 0 时表示不找邻居,自己刷自己的。
如果你是用机械硬盘,你可以将参数设置为 1,这样可以减少随机 IO。如果你使用的是 SSD,建议设置为 0 ,以加快 SQL 的响应时间。
在 8.0 版本中,该参数默认为 0.
小结
在第二章中,介绍了 MySQL 的 WAL 概念,这个机制可以提升数据库的性能,但是也带来了后续内存刷脏页的问题。
- 如果 内存写满 或 redo log 写满,会触发数据库全力刷 脏页。
- 注意设置正确的 innodb_io_capacity ,否则 InnoDB 会大大低估数据库的性能,并显示数据写入的速率。
- 你需要了解 InnoDB 中控制刷脏页速度的机制。
上期问题
上期我留给你的问题是,给一个学号字段创建索引,有哪些方法。
由于这个学号的规则,无论是正向还是反向的前缀索引,重复度都比较高。因为维护的只是一个学校的,因此前面 6 位(其中,前三位是所在城市编号、第四到第六位是学校编号)其实是固定的,邮箱后缀都是 @gamil.com,因此可以只存入学年份加顺序编号,它们的长度是 9 位。
而其实在此基础上,可以用数字类型来存这 9 位数字。比如 201100001,这样只需要占 4 个字节。其实这个就是一种 hash,只是它用了最简单的转换规则:字符串转数字的规则,而刚好我们设定的这个背景,可以保证这个转换后结果的唯一性。
思考题
一个内存配置为 128GB、innodb_io_capacity 设置为 20000 的大规格实例,正常会建议你将 redo log 设置成 4 个 1GB 的文件。
但如果你在配置的时候不慎将 redo log 设置成了 1 个 100M 的文件,会发生什么情况呢?又为什么会出现这样的情况呢?
以上就是本章内容,希望你在生活中可以掌控自己的生活速度。
注:本文章的主要内容来自我对极客时间app的《MySQL实战45讲》专栏的总结,我使用了大量的原文、代码和截图,如果想要了解具体内容,可以前往极客时间
网友评论