美文网首页
12 - 为什么我的 MySQL 性能时好时坏?- 探究脏页的刷

12 - 为什么我的 MySQL 性能时好时坏?- 探究脏页的刷

作者: 天命_风流 | 来源:发表于2020-06-01 00:13 被阅读0次

    关键字

    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讲》专栏的总结,我使用了大量的原文、代码和截图,如果想要了解具体内容,可以前往极客时间

    相关文章

      网友评论

          本文标题:12 - 为什么我的 MySQL 性能时好时坏?- 探究脏页的刷

          本文链接:https://www.haomeiwen.com/subject/luurzhtx.html