zoukankan      html  css  js  c++  java
  • 12 mysql性能抖动

    12 mysql性能抖动

    sql语句为什么变

    在介绍WAL机制时,innodb在处理更新语句的时候,只做了写日志这一个磁盘操作,就是redo log在更新内存写完redo log之后,就返回客户端成功。

    当内存数据页和磁盘数据页内容不一致的时候,称这个内存页为脏页,内存数据写入磁盘后,内存和磁盘上的数据页的内容一致,称为干净页

    平时执行很快的更新操作,其实就是在写日志和内存,而mysql偶尔”抖”一下的那个瞬间,可能就是在刷脏页(flush)。

    触发数据库的flush

    --1 innodb的redo log写满了,这时候系统会停止所有更新操作,把checkpoint往前推进,redo log留出空间可以继续写。
    --2 系统内存不足,当需要新的内存页,而内存不够用的时候,就要淘汰一些数据页,空出内存给别的数据页使用。如果淘汰的是脏页,就要先将脏页写盘
    --3 mysql认为系统”空闲”的时候,也要找时间,只要有机会就刷一点”脏页”
    --4 mysql正常关闭的时候,mysql会把所有内存的脏页都flush到磁盘上。
    

    每个数据页有两种状态

    --一种是在内存里,内存里有肯定是正确的结果,直接返回。
    --另一种是内存里没有数据,就可以肯定数据文件上是正确的结果,读入内存后返回。
    

    第一种”redo log写满了,要flush脏页,这种情况innodb要尽量避免,如果出现这种情况,整个系统就不能再接受更新了,所有的更新就必须堵住,从监控上看,更新数为0.

    第二种”内存不够用了,要先将脏页写到磁盘”,这种情况其实是常态,innodb用缓冲池(buffer pool)管理内存,缓冲池的内存页有三种状态

    --1 还没有使用的
    --2 使用了并且是干净页
    --3 使用了并且是脏页
    

    Innodb是如何区分一个页是脏页还是干净页呢?每个数据页的头部有LSN8个字节,每次修改都会变大,对比这个lsncheckipointlsn,比checkpoint小的一定是干净页。

    Innodb的策略是尽量使用内存,因此对于一个长时间运行的库来说,未被使用的页会很少。

    当要读入的数据不在内存时就必须要缓冲池中申请一个数据页,这时候只能把最久不使用的数据页从内存中淘汰掉,如果要淘汰的是一个干净页,就直接释放出来复用,如果是脏页就必须将脏页刷到磁盘,变成干净页后才能复用。

    但是出现下面的情况会明显影响性能

    --1 一个查询要淘汰的脏页个数太多,会导致查询的响应时间明显变长

    --2 日志写满,更新全部堵住,写性能为0,对敏感业务来说,是明显不能接受的。

    所以,innodb需要有控制脏页比例的结构来避免这样的情况

    Innodb刷脏页的控制策略

    要正确的告诉innodb主机的io能力,这样innodb才能知道需要全力刷脏页的时候,可以多块,参数innodb_io_capacity,这个值建议设置磁盘的IOPS,磁盘的iops可以通过fio这个工具来测试,下面的语句用来测试磁盘随机读写的命令

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

    Innodb的刷盘速度需要参考这两个因素:一个是脏页比例,一个是redo log写盘速度。

    参数innodb_max_dirty_pages_pct是脏页比例上限,默认75%innodbUI根据当前的脏页比例,算出一个范围在0~100直接的数字

    Innodb每次写日志有一个序号,当前写入的需要跟checkpoint对应的序号之间的差值,假设为N,innodb会根据这个N算出一个范围在0~100之间的数字,这个N约大,算出的值就越大。

    要尽量避免业务端感受到mysql”抖”一下,就要合理的设置innodb_io_capacity的值,并且平时要多关注脏页比例,不要经常接近75%。

    脏页比例

    mysql> select VARIABLE_VALUE into @a from global_status where VARIABLE_NAME = 'Innodb_buffer_pool_pages_dirty';
    select VARIABLE_VALUE into @b from global_status where VARIABLE_NAME = 'Innodb_buffer_pool_pages_total';
    select @a/@b;
    (system@127.0.0.1:3306) [information_schema]> select @a/@b;
    +------------------------+
    | @a/@b                  |
    +------------------------+
    | 0.00004577729852902281 |
    +------------------------+
    1 row in set (0.00 sec)
    
    (system@127.0.0.1:3306) [information_schema]> show global status like 'Innodb_buffer_pool_pages_dirty';
    +--------------------------------+-------+
    | Variable_name                  | Value |
    +--------------------------------+-------+
    | Innodb_buffer_pool_pages_dirty | 40    |
    +--------------------------------+-------+
    1 row in set (0.00 sec)
    
    (system@127.0.0.1:3306) [information_schema]> show global status like 'Innodb_buffer_pool_pages_total';
    +--------------------------------+--------+
    | Variable_name                  | Value  |
    +--------------------------------+--------+
    | Innodb_buffer_pool_pages_total | 393208 |
    +--------------------------------+--------+
    1 row in set (0.00 sec)
    
    (system@127.0.0.1:3306) [information_schema]> show variables like 'innodb_io_capacity';
    +--------------------+-------+
    | Variable_name      | Value |
    +--------------------+-------+
    | innodb_io_capacity | 400   |
    +--------------------+-------+
    1 row in set (0.00 sec)
    
    (system@127.0.0.1:3306) [information_schema]> show variables like 'innodb_max_dirty_pages_pct';
    +----------------------------+-------+
    | Variable_name              | Value |
    +----------------------------+-------+
    | innodb_max_dirty_pages_pct | 60    |

    在看一个策略

    一旦一个查询请求需要在执行过程中先flush掉一个脏页时,这个查询就可能比平时慢了,而mysql中的一个机制可能让你的查询会更慢,

    在准备刷一个脏页的时候,如果这个数据页旁边的数据刚好是脏页,就会把这个”邻居”也带着一起刷掉;而这个把邻居一起的逻辑还可以继续蔓延,也就是对于每个邻居数据页,如果跟它相邻的数据页也还是脏页的话,也会被放到一起刷

    innodb中,参数innodb_flush_neighbors用来控制这个行为,值为1的时候有上述刷邻的行为,为0的时候表示不找自己邻居,只刷自己。

    这个找邻的优化在机械硬盘时代还是很有意义,可以减少很多随机的io,机械硬盘的iops一般只有几百,相同的逻辑操作减少随机io就意味着系统性能的大幅度提升,

    而如果使用ssd这类iops较高的设备的话,建议把该参数设置为0,在mysql 8.0,参数innodb_flush_neighbors默认为0.

    思考题

    一个内存未128GB,innodb_io_capacity设置为20000的大规格实例,正常建议会将redo log设置为41GB文件,但是如果在配置的时候不慎将redo log设置成了1100M的文件,会发生什么情况?又为什么会出现这样的情况呢?

  • 相关阅读:
    面试问题记录
    面试问题记录
    面试问题记录
    JavaScript => ?
    Jsr303数据校验
    在浏览器上开发GO和Vue!(基于code-server)
    IdentityServer4 4.0.0
    9/13-9/18
    9/6-9/10
    8/30-9/3
  • 原文地址:https://www.cnblogs.com/yhq1314/p/11319737.html
Copyright © 2011-2022 走看看