当前位置: 首页 > 后端技术 > Java

为什么我的MySQL有点“抖动”?

时间:2023-04-01 22:18:57 Java

在平时的工作中,不知道大家有没有遇到过这样的场景。一条SQL语句在正常执行的时候是非常快的,但是有时候不知道怎么回事就变的很慢,而且像这样的场景是很难重现的,不仅是随机的,而且持续的时间也很短。为什么你的SQL语句变“慢”要分析SQL语句变慢的原因,首先我们需要了解MYSQL的WAL机制。MySQL中经常提到的WAL技术,WAL的全称是Write-AheadLogging,它的要点是先写入日志,再写入磁盘。InnoDB引擎首先将记录写入重做日志(redolog)。重做日志在哪里?它也在磁盘上。这也是一个写入磁盘的过程,但是与更新过程不同的是,更新过程是对磁盘进行RandomIO,比较耗时。而写redolog是磁盘上的顺序IO。效率应该很高。数据页修改完成后,在脏页被刷出磁盘之前写入重做日志。注意先修改数据,再写入日志。重做日志在数据页之前被写回磁盘。当内存数据页的内容与磁盘数据页不一致时,我们称这个内存页为“脏页”。内存数据写入磁盘后,数据页在内存中和磁盘上的内容是一致的,称为“干净页”。知道了这些,我们再回到最初的问题,我们的MYSQL为什么会抖动?不难想象,快速更新操作通常是写内存和日志,而MySQL偶尔“抖动”的瞬间,可能是在刷脏页。这就引出了一个新的问题,什么会触发数据库的flush过程?1、redolog满了;2、有新内存需要读入内存,内存不够用。这时候就需要刷新脏业务;3.系统空闲时;第一种页面是“重做日志已满,需要刷新脏页”,这是InnoDB应该尽量避免的。因为当这种情况发生时,整个系统就不能再接受更新了,所有的更新都必须被阻止。如果从监视器上看,此时更新次数会降为0。二是“内存不够,必须先将脏页写入磁盘”,这种情况其实很正常。InnoDB使用缓冲池来管理内存。缓冲池中的内存页有三种状态:第一种是还没有被使用;第二个是它们被使用并且是干净的页面;三是它们被使用并且是脏页。InnoDB的策略是尽可能多地使用内存,所以对于一个长时间运行的库来说,未使用的页面是非常少的。刷脏页的参数控制是innodb_io_capacity,可以使用showglobalvariableslike'%io_cap%'查询mysql>showglobalvariableslike'%io_cap%';+----------------------+--------+|变量名|价值|+------------------------+--------+|innodb_io_capacity|200||innodb_io_capacity_max|2000|+--------------------+--------+2行在集合中(0.02秒)使用setglobalinnodb_io_capacity=2000;设置全局innodb_io_capacity_max=4000;来控制刷脏页的时间。当要读取的数据页不在内存中时,必须在缓冲池中申请一个数据页。这时候只能从内存中剔除最久未被使用的数据页:如果要剔除一个cleanpage,直接释放再用;但如果是脏页,则必须先将脏页刷入磁盘,变成干净页后再使用。因此,flushdirtypages虽然很正常,但是以下两种情况会明显影响性能:一个query有太多的dirtypages需要剔除,会导致queryresponsetime明显变长;日志满了,所有更新都被阻塞,写入性能降为0,这种情况对于敏感业务来说是不能接受的。MySQL中的一个机制可能会让你的查询变慢:即使是坐着的,也就是你刷新自己的时候,如果发现邻居也是脏页,你就会一起flush,这种行为会一直延续下去。在InnoDB中,innodb_flush_neighbors参数用于控制此行为。当值为1时,就会有上述的“坐在一起”机制。当值为0时,表示不找邻居,冲自己的。在MySQL8.0中,innodb_flush_neighbors参数的默认值已经是0。mysql>showglobalvariableslike'%innodb_flush_neighbors%';+------------------------+--------+|变量名|值|+------------------------+--------+|innodb_flush_neighbors|0|+------------------------+------+1行在集合中(0.00秒)