zoukankan      html  css  js  c++  java
  • MySQL抖动的原因

    参考:

    https://blog.csdn.net/fouy_yun/article/details/103056296

    https://blog.csdn.net/qq_41086588/article/details/104372807

    https://www.jianshu.com/p/6ffb01aa7717

    MySQL抖动的原因

    我们在使用MySQL实现业务处理的时候,更多的关注可能在SQL本身上面是不是最优的。今天我们从一个很小的点去看一下MySQL的实现原理,那就是MySQL刷脏页相关的问题。 

    刷脏页

    在MySQL中,如果内存数据页(buffer pool)和磁盘数据不一致时,这个内存数据页我们认为是“脏页”;当内存数据页 flush 到磁盘之后,内存数据页和磁盘数据一致时,此时这个内存数据页就称为“干净页”。(内存中的数据和磁盘一致就可以认为是干净页)刷脏页的过程可以参见如下所示:
    在这里插入图片描述
    从上图可以看出,更新数据时,内存更新完成+redo log 写完之后就算成功了。但是此时内存中是存在脏页的,也就是数据还没有同步到磁盘上。第二个过程就是将内存中的数据同步到磁盘上面。

    触发刷脏页的条件

    1. redo log 写满了。因为 redo log 是一块固定大小的磁盘空间,当redo log 写满之后,会强制触发磁盘的脏页 flush 。

    2. 系统内存不足。因为数据的更新会更新 内存页+redo log,此时有一个空间不足都会触发脏页 flush。

    3. 系统负载不高时触发。也就是系统空闲时可以 flush 一些脏页,以应对后续可能出现的高负载。

    4. MySQL正常关闭。数据库正常关闭时,此时所有的数据肯定需要写入磁盘持久化。

    从上面的几个条件我们可以看出,对于MySQL 中的更新数据,主要有以下 2 种状态:1、内存中有(可能是脏页或者干净页)数据,此时更新操作只需要更新内存 + 写入 redo log;2、内存中无对应的数据,此时就需要从磁盘中读取对应的数据,然后更新内存 + 写入 redo log。

    对于上面可能出现MySQL抖动的条件,我们主要看前面 2 种。

    • 对于第一种,当 redo log 写满的时候,MySQL就会拒绝服务,此时不会接受任何更新操作。因此这种情况是 MySQL 所需要避免的。

    • 对于第二种,就是内存不够用了,这种情况出现的概率还是比较大的。InnoDB 使用 Buffer Pool 管理内存,对于 Buffer Pool 中的内存主要有以下几种状态:未被使用;已使用且是干净页;已使用且是脏页。

    因此,当内存不足时,如果要申请一个内存页时,此时就需要淘汰一个最久不使用的内存页:如果是“干净页”,则直接释放出来使用;如果是“脏页”则首先需要 flush 然后再释放使用

    总结以上,MySQL发生抖动的原因主要有以下:

    1. 内存不足时,所需要淘汰的内存页(脏页)太多;

    2. redo log 写满时,这种可能出现瞬间的拒绝服务。

    InnoDB 的 flush 脏页策略

    innodb_io_capacity 参数,它会告诉 InnoDB 你的磁盘读写能力是多少。这个参数建议设置成机器的 IOPS ,可以通过 fio 工具来测试获得。如下所示:

    $ fio -filename=data.mp4 -direct=1 -iodepth 1 -thread -rw=randrw -ioengine=psync -bs=16k -size=500M -numjobs=10 -runtime=10 -group_reporting -name=mytest
    

    测试完成之后如下图所示:

    在这里插入图片描述
    如果 innodb_io_capacity 设置的过小,比如比正常刷脏页的速度还慢,则肯定影响正常的IO 读写。

    • innodb_max_dirty_pages_pct 参数: 脏页的比例,默认是 75%。

    • innodb_flush_neighbors 参数:刷脏页的时候,如果相邻的内存页也是脏页的话,也会flush。这个参数在机械硬盘时代,可能会减少随机IO 的开销。使用 SSD 的话建议设置成 0 ,即关闭。


    参考:《极客时间:MySQL实战》、《高性能MySQL》
    链接:http://moguhu.com/article/detail?articleId=122

    线上Mysql为什么会出现性能抖动和相应的解决办法

    1.基础知识

    1.Innodb处理更新语句的流程

    (1).首先Server层的执行器调用InnoDB数据接口,请求数据 --Server层
    (2).InnoDB会先判断该数据**是否存在于内存**当中,如果在,走(3),如果不在则走(4) --InnoDB引擎层
    (3).将数据返回给Server层 --InnoDB引擎层
    (4).加载数据到内存,并将数据返回到Server层 --InnoDB引擎层
    (5).Server层的执行器对数据进行修改并调用引擎的写入接口 --Server层
    (6).InnoDB将数据更新应用到内存 --InnoDB引擎层
    (7).写入redo log日志(事务处于prepare状态)  --InnoDB引擎层
    (8).写入binlog日志(Server层维护的日志,用于归档和主备,数据恢复) --Server层
    (9).提交事务,事务处于commit状态  --InnoDB引擎层

    从上述的数据更新过程当中,我们可以看到InnoDB在做数据更新时,只进行了一次磁盘操作,即写redo log日志。而由于redo log日志是以append-only的方式写入的,避免了随机写,所以效率很高

    2.简单介绍redo log和binlog

    redo log是由InnoDB引擎维护的日志,可以保证数据库的crash-safe。这是由于redo log记录了数据页的物理变化,可以在数据库发生异常关机或者在重启时,通过重放redo log,保证数据库数据的正确性

    binlog日志则是由Server层维护的日志,可供所用引擎使用。binlog主要作为逻辑日志用于归档,有mixed,statement,row三种格式。可用作故障恢复,主被复制,主从模型等等高可用架构。

    binlog 和 redo log的不同之处在于:

    • binlog是由Server层维护的,可供所有引擎共同使用。而redo log则是由InnoDB引擎维护的,以插件形式提供给Mysql使用的。
    • binlog主要作为逻辑日志用作归档,而没有crash-safe功能。
    • binlog是追加写的,并没有大小的限制,写满一个文件之后可生成新的日志文件继续记录,即可生成多个binlog日志文件。而redo log是一组固定大小的日志文件,当日志文件写满之后会从头循环写

    redo log通过减少随机写入提高了Mysql的性能。

    3.为什么会出现性能抖动?

    有什么我们在线上会发现线上数据库操作会发生性能上的抖动,查看数据库执行语句,发现也正常使用了索引。并且这种情况往往是随机的,很难复现。出现这种情况,我们往往可以考虑,是否是由于Mysql刷新脏页的动作导致的

    由于在已经回顾了redo log的知识,我们可以探索有那些情况会触发刷新脏页的动作。

    • redo log写满了。redo log写满了,也就意味着write_pos追上了checkpoint。这个时候,会造成阻塞所有操作,并进行刷新脏页的动作。由于会造成线上业务的暂时停摆,所以要尽量避免这种情况。
    • 内存空间用完了。我们知道,InnoDB维护了InnoDB Buffer Pool来将数据以数据页的形式(每个数据页16KB)加载到内存当中,以提供Mysql的效率。但是如果内存空间用完了,也会造成Mysql刷新脏页的动作。
    • 在Mysql系统空闲时,会通过后台线程刷新脏页。
    • 在Mysql正常关闭的时候,会将内存当中的所有脏页刷到磁盘当中。

    在这里由于第三,第四种情况属于正常情况,我们在这里不做讨论。而第一种情况,我们可以根据系统的性能指标,通过适当的增大redo log的大小来进行改善。因此,在这里我们主要讨论第二中情况。

    什么是脏页?

    脏页,在Mysql当中指的是内存当中与磁盘当中数据不一直的数据页(注意这里描述的是内存当中的数据页)。
    那么在InnoDB的缓存池当中有三种状态的数据页:即脏页,干净页,未使用页。

    而当要读入的数据没有在内存当中的时候,就会去InnoDB缓冲池当中申请一个数据页,如果内存不够,InnoDB缓冲池就只能将最不经常使用的数据页淘汰。如果淘汰的是干净页,则可以直接使用。而如果淘汰的是脏页,就必须先进行flush操作,使脏页变成干净页才能使用。因此,如果一个查询语句需要淘汰大量的脏页,就会造成Mysql性能抖动的现象。

    如何控制InnoDB刷新脏页的策略?

    首先要理解一个参数:innodb_io_capacity。这个参数是告诉innodb,当系统需要全力刷新脏页时,可以刷的多快。而这个参数一般建议设置为IOPS(IOPS可以通过fio工具进行磁盘测试得到)。

    如果对于一个固态硬盘的innodb_io_capacity设置的过小,就可能会导致InnoDB脏页的生成速度远大于脏页的刷新速度,但磁盘的io负载并不高的情况。但是,我们InnoDB又不会完全按照innodb_io_capacity所设置的值一直全力刷新脏页。而是会根据一定规则,计算出一个平均值R,以innodb_io_capacity * R% 的能力刷新脏页,这是比较合理的。这是因为InnodbDB有很多的磁盘操作,比如从磁盘当中查询数据,不能把所有的io能力都放在了刷新脏页上。

    因此,如果线上数据库发生了性能抖动问题,而通过慢sql的排查,发现sql可以正常的使用索引,我们可以怀疑是因为Mysql的脏页刷新机制导致的。具体的解决方案,我们可以通过适当的增大redo log的大小,并且合理的设置innodb_io_capacity 的参数值来改善。

    二 有可能是Mysql有后台线程进行备份

    对于Mysql的备份可以分为冷备和热备。而mysqldump则一般作为冷备的手段,可以在业务低峰期间进行逻辑备份。为了保证并发度和数据的一致性,一般在进行备份的时候会使用–single-trasaction来开启一个事务,利用Mysql的可重复读事务隔离级别的一致性视图来保证数据的一致性。
    但是,使用mysqldump进行备份时,也有可能造成线上业务的性能抖动。因为Mysql需要将磁盘当中的数据全部都加载到内存当中,这就需要Buffer Pool利用内存淘汰机制淘汰一部分数据页。而这些数据页则很有可能是热点数据。而对于线上业务而言,大量的请求达到引擎之后,发现数据不在内存当中就需要去磁盘上加载数据页,但是内存却一直被更新线程占用,造成大量数据页等待flush,因此整个线上业务就可能被假阻塞。更加可怕的是,对于磁盘大量IO的影响是暂时性的,在后台线程备份结束之后就可以结束,而由于大量热点数据被刷出内存造成的内存命中率降低而导致的影响则是长远的。需要线上业务长时间运行在能够逐渐恢复

    三 有可能是线上业务在等锁

    对于线上业务性能抖动,还有一种可能就是线上的查询被锁给阻塞了。在这里有可能的阻塞分别为MDS锁和行锁。对于MDS锁来说非常好排查,我们可以使用show processlist命令去看线程状态。
    我们可以看到,在State下,目标线程处于waiting for table metadata lock的状态
    由于我们在show processlist只能看到被阻塞线程的详情,对于到底是那个表占有了MDS锁却一无所知。而具体的排查方法,在有了performance_schema库和sys库之后变得更加方便了。我们可以在数据库启动时设置performance_schema=ON(Mysql5.6之后默认开启),并且在sys.schema_table_lock_waits表中查看详细数据。而对于行锁的排查,我们则可以到sys.innodb_lock_waits表中查看其详细信息这里不展开描述。

    四 有可能是长事务导致的性能抖动

    我们知道在Mysql中实现了四种隔离级别:读未提交,读已提交,可重复读,序列化。而MVCC则是为了实现读已提交,可重复读两种隔离级别而产生的版本控制方法。在可重复读中,Mysql会在事务启动时开启一个一致性视图,记录下当前的事务id。若有其他的并发事务对这个数据行进行了修改,则会将其修改过程记录到undo log当中,当该事务进行一致性读的时候,会应用undo log来生成该事务对应的数据版本,一次来保证事务的隔离级别。
    在长事务当中,大量的undo log 无法得到释放,并且如果对于某一行会有大量的并发事务进行操作,那么在该事务进行读取时,则会应用大量的Undo log来计算当前事务所能看到的数据版本,则也可能时造成线上业务性能抖动的原因。

    附录:

    • mysql脏页:
      当内存数据页和磁盘数据页上的内容不一致时,我们称这个内存页为脏页;
      内存数据写入磁盘后,内存页上的数据和磁盘页上的数据就一致了,我们称这个内存页为干净页。

    • 刷脏页的时机:
      (1)redo log写满时,没有看见了,此时需要将checkpoint向前推进,推进的这部分日志对应的脏页刷入到磁盘,此时所有的更新全部阻塞,此时写的性能变为0,必须待刷一部分脏页后才能更新。
      (2)系统内存不足时,需要将一部分数据页淘汰掉,如果淘汰的是脏页,需要先将脏页同步到磁盘。
      (3)MySQL认为空闲的时间,这种没有性能问题。
      (4) mysql正常关闭之前,会把所有脏页刷入磁盘,不存在性能问题。


    作者:Lee_8f69
    链接:https://www.jianshu.com/p/6ffb01aa7717
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 相关阅读:
    python 进度条
    linux中利用Shell脚本实现自动安装部署weblogic服务
    Linux虚拟机如何上网
    常用Python脚本
    Allure测试框架
    软件评测师 第二小时
    保险项目测试流程(一)
    电子商务网站测试总结
    Python随机生成电话号码&号码段分析
    Python中的*args和**kwargs
  • 原文地址:https://www.cnblogs.com/xuwc/p/14039413.html
Copyright © 2011-2022 走看看