Mysql - 脏页刷新机制

什么是脏页?

     Mysql InnoDB表基本都是基于B+树索引进行存储的,而数据存储的最小单元就是数据页(物理存储结构:表 -> 表空间(和索引)-> 段 -> 区 -> 页 -> 记录行 -> 列等)。而当内存中的数据页与磁盘中的不一致时该数据页叫作脏页,当执行flush操作将磁盘页数据页和内存数据页进行合并之后,内存和磁盘的数据页相同,则称为干净页。任何时刻 InnoDB三大特性之Buffer Pool缓冲池 中的数据页都可能存在三种情况:

  1. 还没有使用的数据页;
  2. 使用了但是是干净页;
  3. 使用了是脏页;

为什么会出现脏页?

    Mysql - 普通索引与唯一索引之间性能差别change buffer中理解,当执行 select、update、delete操作时,InnoDB使用 change buffer进行加速写操作,可以将写操作的随机磁盘访问调整为局部顺序操作,而随机磁盘访问是数据库操作中性能影响比较大的点。当非唯一索引记录的缓存页发生写操作时,修改完成change buffer就可以立刻返回了。 具体理解写操作需要知道其发生的环境操作:Mysql - InnoDB三大特性之双写缓冲区(Double Write Buffer)binlog + redolog的二阶段提交

 

    当我们需要执行一个简单的查询操作时,可能发生抖动,即某次查询的耗时邹增,外在整体表现就是 tp50非常低,tp 999等非常高的现象。就是因为执行某查询操作时,发生了刷脏页的操作,并且更可怕的是发生了多次间接的连坐(innodb_flush_neighbors)。所以我们需要知道脏页的刷新机制,已经可能存在的抖动点。

脏页刷新的操作时机?

  1. InnoDB的redo log日志写满了,此时操作系统不得不停止所有更新操作,将check point往前推,redo log可以留出更多的空间,并把该部分的脏页刷新到磁盘中(flush动作期间该段不可写)。
  2. 系统内存不足,需要新的数据页但是发现需要淘汰部分数据页,而数据页存在脏页,则需要先执行flush操作,才能进行淘汰。
  3. 后端线程刷新脏页,即Buffer Pool中的 page cleaner Thread。
  4. Mysql关闭实例时,肯定是要刷新所有脏页的。

    Info:redo log的存在一方面宏观上是为了保证事务的ACID特性,另一方面底层是 binlog + redolog的二阶段提交、双写缓存区的问题。而redo log实现了Mysql的WAL(Write-Ahead Logging)技术,先写日志再写磁盘,具体来说,当有一条记录需要更新的时候,InnoDB 引擎就会先把记录写到 redo log里面,并更新内存,这个时候更新就算完成了。磁盘刷新就是脏页刷新机制。redo log是一块固定大小的文件,默认是根据参数:innodb_log_files_in_group:redo log配置日志文件数量;innodb_log_file_size:redo log 每个日志文件的大小;一般配置4个 1G大小的redo 日志。 如下图:

    redo log是一个环形,从头开始写,当写到末尾则继续从开头继续写。write pos是当前记录的位置,一边写一边后移,checkpoint 是当前要擦除的位置,也是往后进行推移,擦除前需要保证数据被写入文件。量点之间的部分就是剩余的空间,如图所示,当write pos 马上追赶上 write point时就需要:停止更新,把write pos往前推进以下,擦除一些记录,持久化到磁盘。redo log使用WAL技术保证,InnoDB即使发生了数据库异常重启,之前的数据也不会发生丢失,拥有了crash safe的能力。

 

刷脏页时间如果比较长,对性能的影响

  1. 一个查询淘汰的脏页数据过多,则会导致查询的响应时间瞬时过长【跟其他的比】。
  2. 日志写满,所有的更新操作全部阻塞,写性能瞬间跌为0,对某些业务场景是不能接受的。

 

InnoDB脏页刷新策略

   知道了脏页刷新的触发时机,除了后台线程刷新,其他几点可以理解成被动执行,但是当发生被动执行时对业务就会有以上的影响(读、写耗时很长),所以最好是在业务不繁忙的时候就使用 bg线程执行刷新。刷新策略与可能存在的阻塞点就是需要明确的点,脏页刷新首先就与磁盘的写入能力(寻址)直接相关,如果是机械硬盘或ssd肯定完全不同, 并且Mysql也不知道该服务是否还运行着其他业务,所以不能完全执行Mysql自己暂用所有的 磁盘io能力。innoDB允许使用参数 innodb_io_capacity进行设置磁盘刷新的页数。 该值可以设置为磁盘的 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 

比如:

[root@iZ2ze4zhiwnwqxsc7wehwlZ mysql]# yum install fio
...
[root@iZ2ze4zhiwnwqxsc7wehwlZ mysql]# fio -filename=/usr/share/mysql/errmsg-utf8.txt -direct=1 -iodepth 1 -thread -rw=randrw -ioengine=psync -bs=16k -size=500M -numjobs=10 -runtime=10 -group_reporting -name=mytest 
mytest: (g=0): rw=randrw, bs=(R) 16.0KiB-16.0KiB, (W) 16.0KiB-16.0KiB, (T) 16.0KiB-16.0KiB, ioengine=psync, iodepth=1
...
fio-3.7
Starting 10 threads
mytest: Laying out IO file (1 file / 500MiB)
Jobs: 10 (f=10): [m(10)][100.0%][r=16.7MiB/s,w=16.8MiB/s][r=1068,w=1073 IOPS][eta 00m:00s]
mytest: (groupid=0, jobs=10): err= 0: pid=22332: Sun Apr 11 15:58:07 2021
   read: IOPS=1055, BW=16.5MiB/s (17.3MB/s)(165MiB/10004msec)
    clat (usec): min=117, max=67536, avg=4523.39, stdev=10660.34
     lat (usec): min=117, max=67536, avg=4523.53, stdev=10660.35
    clat percentiles (usec):
     |  1.00th=[  258],  5.00th=[  273], 10.00th=[  281], 20.00th=[  306],
     | 30.00th=[  351], 40.00th=[  570], 50.00th=[ 2024], 60.00th=[ 2966],
     | 70.00th=[ 3687], 80.00th=[ 4555], 90.00th=[ 6063], 95.00th=[ 8979],
     | 99.00th=[56361], 99.50th=[57410], 99.90th=[58983], 99.95th=[60031],
     | 99.99th=[67634]
   bw (  KiB/s): min= 1408, max= 2016, per=10.00%, avg=1689.73, stdev=106.97, samples=198
   iops        : min=   88, max=  126, avg=105.58, stdev= 6.70, samples=198
  write: IOPS=1089, BW=17.0MiB/s (17.9MB/s)(170MiB/10004msec)
    clat (usec): min=352, max=65767, avg=4789.51, stdev=11117.17
     lat (usec): min=353, max=65768, avg=4790.16, stdev=11117.19
    clat percentiles (usec):
     |  1.00th=[  408],  5.00th=[  412], 10.00th=[  416], 20.00th=[  420],
     | 30.00th=[  433], 40.00th=[  502], 50.00th=[ 2147], 60.00th=[ 3064],
     | 70.00th=[ 3752], 80.00th=[ 4621], 90.00th=[ 6063], 95.00th=[10028],
     | 99.00th=[55837], 99.50th=[56886], 99.90th=[60556], 99.95th=[62653],
     | 99.99th=[63177]
   bw (  KiB/s): min= 1024, max= 2528, per=9.99%, avg=1740.94, stdev=288.07, samples=198
   iops        : min=   64, max=  158, avg=108.78, stdev=18.00, samples=198
  lat (usec)   : 250=0.28%, 500=39.14%, 750=3.38%, 1000=0.89%
  lat (msec)   : 2=5.78%, 4=23.75%, 10=21.94%, 20=0.19%, 50=0.93%
  lat (msec)   : 100=3.73%
  cpu          : usr=0.08%, sys=0.48%, ctx=41252, majf=0, minf=13
  IO depths    : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     issued rwts: total=10561,10900,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=1

Run status group 0 (all jobs):
   READ: bw=16.5MiB/s (17.3MB/s), 16.5MiB/s-16.5MiB/s (17.3MB/s-17.3MB/s), io=165MiB (173MB), run=10004-10004msec
  WRITE: bw=17.0MiB/s (17.9MB/s), 17.0MiB/s-17.0MiB/s (17.9MB/s-17.9MB/s), io=170MiB (179MB), run=10004-10004msec

Disk stats (read/write):
  vda: ios=10455/10782, merge=0/2, ticks=8823/9527, in_queue=18539, util=97.54%

    刷磁盘速度主要依赖两个因素:脏页的比例redo log的写速度。 InnoDB允许使用参数 innodb_max_dirty_pages_pct 设置脏页的比例,超过该比例就触发刷新机制,默认值为 75%。InnoDB会根据当前的脏页比(比如当前值为M),计算出一个范围为【0~100】的值N, 在根据 当前日志的序号与 checkpoint的差值(比如为N)进行复杂的计算得到一个速度比,整体比较复杂度,如下图:

    即刷新脏页的策略与下列因素有关【整体就是脏页比例、redolog写的速度】:

  1. 当前的脏页比例值;
  2. 我们设置的最大脏页比例值,innodb_max_dirty_pages_pct;
  3. 当前的日志的序号;
  4. 当前的checkpoint;
  5. 我们告诉Mysql的允许使用的刷盘能力值 innodb_io_capacity 个页;

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值