Day876.redolog刷盘问题 -MySQL实战

news2024/12/25 1:29:08

redolog刷盘问题

Hi,我是阿昌,今天学习记录的是关于redolog刷盘问题的内容。

平时的工作中,一条 SQL 语句,正常执行的时候特别快,但是有时也不知道怎么回事,它就会变得特别慢,并且这样的场景很难复现,它不只随机,而且持续时间还很短。

看上去,这就像是数据库“抖”了一下


一、你的 SQL 语句为什么变“慢”了

binlog&redoLog,WAL 机制

InnoDB 在处理更新语句的时候,只做了写日志这一个磁盘操作。这个日志叫作 redo log(重做日志),也就是《孔乙己》里咸亨酒店掌柜用来记账的粉板,在更新内存写完 redo log 后,就返回给客户端,本次更新成功。

做下类比的话,掌柜记账的账本是数据文件,记账用的粉板是日志文件(redo log),掌柜的记忆就是内存。

掌柜总要找时间把账本更新一下,这对应的就是把内存里的数据写入磁盘的过程,术语就是 flush

在这个 flush 操作执行之前,孔乙己的赊账总额,其实跟掌柜手中账本里面的记录是不一致的。

因为孔乙己今天的赊账金额还只在粉板上,而账本里的记录是老的,还没把今天的赊账算进去。

当内存数据页跟磁盘数据页内容不一致的时候,称这个内存页为“脏页”。

内存数据写入到磁盘后,内存和磁盘上的数据页的内容就一致了,称为“干净页”。

不论是脏页还是干净页,都在内存中。

在这个例子里,内存对应的就是掌柜的记忆。

接下来,用一个示意图来展示一下“孔乙己赊账”的整个操作过程。

假设原来孔乙己欠账 10 文,这次又要赊 9 文。

图 1 “孔乙己赊账”更新和 flush 过程

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

那么,什么情况会引发数据库的 flush 过程呢?

用咸亨酒店掌柜的这个例子,想一想:

掌柜在什么情况下会把粉板上的赊账记录改到账本上?

  • 第一种场景是,粉板满了,记不下了。这时候如果再有人来赊账,掌柜就只得放下手里的活儿,将粉板上的记录擦掉一些,留出空位以便继续记账。当然在擦掉之前,他必须先将正确的账目记录到账本中才行。这个场景,对应的就是 InnoDB 的 redo log 写满了。这时候系统会停止所有更新操作,把 checkpoint 往前推进,redo log 留出空间可以继续写。redo log 的示意图,这里我改成环形,便于理解。

图 2 redo log 状态图

checkpoint 可不是随便往前修改一下位置就可以的。比如图 2 中,把 checkpoint 位置从 CP 推进到 CP’,就需要将两个点之间的日志(浅绿色部分),对应的所有脏页都 flush 到磁盘上。之后,图中从 write pos 到 CP’之间就是可以再写入的 redo log 的区域。

  • 第二种场景是,这一天生意太好,要记住的事情太多,掌柜发现自己快记不住了,赶紧找出账本把孔乙己这笔账先加进去。这种场景,对应的就是系统内存不足。当需要新的内存页,而内存不够用的时候,就要淘汰一些数据页,空出内存给别的数据页使用。如果淘汰的是“脏页”,就要先将脏页写到磁盘。这时候难道不能直接把内存淘汰掉,下次需要请求的时候,从磁盘读入数据页,然后拿 redo log 出来应用不就行了?这里其实是从性能考虑的。如果刷脏页一定会写盘,就保证了每个数据页有两种状态:

    • 一种是内存里存在,内存里就肯定是正确的结果,直接返回;
    • 另一种是内存里没有数据,就可以肯定数据文件上是正确的结果,读入内存后返回。这样的效率最高。
  • 第三种场景是,生意不忙的时候,或者打烊之后。这时候柜台没事,掌柜闲着也是闲着,不如更新账本。这种场景,对应的就是 MySQL 认为系统“空闲”的时候。当然,MySQL“这家酒店”的生意好起来可是会很快就能把粉板记满的,所以“掌柜”要合理地安排时间,即使是“生意好”的时候,也要见缝插针地找时间,只要有机会就刷一点“脏页”。

  • 第四种场景是,年底了咸亨酒店要关门几天,需要把账结清一下。这时候掌柜要把所有账都记到账本上,这样过完年重新开张的时候,就能就着账本明确账目情况了。这种场景,对应的就是 MySQL 正常关闭的情况。这时候,MySQL 会把内存的脏页都 flush 到磁盘上,这样下次 MySQL 启动的时候,就可以直接从磁盘上读数据,启动速度会很快。


上面四种场景对性能的影响。其中,第三种情况是属于 MySQL 空闲时的操作,这时系统没什么压力,而第四种场景是数据库本来就要关闭了。

这两种情况下,不会太关注“性能”问题。所以这里,主要来分析一下前两种场景下的性能问题。

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

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

  • 第一种是,还没有使用的;
  • 第二种是,使用了并且是干净页;
  • 第三种是,使用了并且是脏页。

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

而当要读入的数据页没有在内存的时候,就必须到缓冲池中申请一个数据页。

这时候只能把最久不使用的数据页从内存中淘汰掉

如果要淘汰的是一个干净页,就直接释放出来复用;

但如果是脏页呢,就必须将脏页先刷到磁盘,变成干净页后才能复用。


所以,刷脏页虽然是常态,但是出现以下这两种情况,都是会明显影响性能的:

  1. 一个查询要淘汰的脏页个数太多,会导致查询的响应时间明显变长;
  2. 日志写满,更新全部堵住,写性能跌为 0,这种情况对敏感业务来说,是不能接受的。

所以,InnoDB 需要有控制脏页比例的机制,来尽量避免上面的这两种情况。


二、InnoDB 刷脏页的控制策略

InnoDB 脏页的控制策略,以及和这些策略相关的参数。

首先,要正确地告诉 InnoDB 所在主机的 IO 能力,这样 InnoDB 才能知道需要全力刷脏页的时候,可以刷多快。

这就要用到 innodb_io_capacity 这个参数了,它会告诉 InnoDB 你的磁盘能力。

这个值建议设置成磁盘的 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_io_capacity 参数,而导致的性能问题也比比皆是。

一个库的性能问题,说 MySQL 的写入速度很慢,TPS 很低,但是数据库主机的 IO 压力并不大。经过一番排查,发现罪魁祸首就是这个参数的设置出了问题。他的主机磁盘用的是 SSD,但是 innodb_io_capacity 的值设置的是 300。于是,InnoDB 认为这个系统的能力就这么差,所以刷脏页刷得特别慢,甚至比脏页生成的速度还慢,这样就造成了脏页累积,影响了查询和更新性能。

虽然现在已经定义了“全力刷脏页”的行为,但平时总不能一直是全力刷吧?

毕竟磁盘能力不能只用来刷脏页,还需要服务用户请求。

所以接下来,就一起看看 InnoDB 怎么控制引擎按照“全力”的百分比来刷脏页。


根据前面提到的知识点,试想一下,如果来设计策略控制刷脏页的速度,会参考哪些因素呢?

这个问题可以这么想,如果刷太慢,会出现什么情况?

首先是内存脏页太多,其次是 redo log 写满。所以,InnoDB 的刷盘速度就是要参考这两个因素:

  • 一个是脏页比例
  • 一个是 redo log 写盘速度

InnoDB 会根据这两个因素先单独算出两个数字。


参数 innodb_max_dirty_pages_pct 是脏页比例上限,默认值是 75%

InnoDB 会根据当前的脏页比例(假设为 M),算出一个范围在 0 到 100 之间的数字,计算这个数字的伪代码类似这样:

F1(M)
{
  if M>=innodb_max_dirty_pages_pct then
      return 100;
  return 100*M/innodb_max_dirty_pages_pct;
}

InnoDB 每次写入的日志都有一个序号,当前写入的序号跟 checkpoint 对应的序号之间的差值,假设为 N。InnoDB 会根据这个 N 算出一个范围在 0 到 100 之间的数字,这个计算公式可以记为 F2(N)。F2(N) 算法比较复杂,只要知道 N 越大,算出来的值越大就好了

然后,根据上述算得的 F1(M) 和 F2(N) 两个值,取其中较大的值记为 R,之后引擎就可以按照 innodb_io_capacity 定义的能力乘以 R% 来控制刷脏页的速度。

上述的计算流程比较抽象,不容易理解,所以画了一个简单的流程图。

图中的 F1、F2 就是上面通过脏页比例和 redo log 写入速度算出来的两个值。

图 3 InnoDB 刷脏页速度策略
InnoDB 会在后台刷脏页,而刷脏页的过程是要将内存页写入磁盘。

所以,无论是查询语句在需要内存的时候可能要求淘汰一个脏页,还是由于刷脏页的逻辑会占用 IO 资源并可能影响到了更新语句,都可能是造成从业务端感知到 MySQL“抖”了一下的原因。

要尽量避免这种情况,就要合理地设置 innodb_io_capacity 的值,并且平时要多关注脏页比例,不要让它经常接近 75%

其中,脏页比例是通过 Innodb_buffer_pool_pages_dirty/Innodb_buffer_pool_pages_total 得到的,具体的命令参考下面的代码:

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;

一旦一个查询请求需要在执行过程中先 flush 掉一个脏页时,这个查询就可能要比平时慢了。

MySQL 中的一个机制,可能让查询会更慢:

在准备刷一个脏页的时候,如果这个数据页旁边的数据页刚好是脏页,就会把这个“邻居”也带着一起刷掉;

而且这个把“邻居”拖下水的逻辑还可以继续蔓延,也就是对于每个邻居数据页,如果跟它相邻的数据页也还是脏页的话,也会被放到一起刷。在 InnoDB 中,innodb_flush_neighbors 参数就是用来控制这个行为的,值为 1 的时候会有上述的“连坐”机制,值为 0 时表示不找邻居,自己刷自己的。

找“邻居”这个优化在机械硬盘时代是很有意义的,可以减少很多随机 IO。

机械硬盘的随机 IOPS 一般只有几百,相同的逻辑操作减少随机 IO 就意味着系统性能的大幅度提升。而如果使用的是 SSD 这类 IOPS 比较高的设备的话,我就建议把 innodb_flush_neighbors 的值设置成 0

因为这时候 IOPS 往往不是瓶颈,而“只刷自己”,就能更快地执行完必要的刷脏页操作,减少 SQL 语句响应时间。

在 MySQL 8.0 中,innodb_flush_neighbors 参数的默认值已经是 0 了。


三、总结

利用 WAL 技术,数据库将随机写转换成了顺序写,大大提升了数据库的性能。

但是,由此也带来了内存脏页的问题。脏页会被后台线程自动 flush,也会由于数据页淘汰而触发 flush,而刷脏页的过程由于会占用资源,可能会让你的更新和查询语句的响应时间长一些。

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

内存128G,所以内存不是瓶颈; innodb_io_capacity设置成20000系统会认为磁盘IO能力很好;

redo log设置成100M太小了,redo log很容易写满而导致系统停下来去刷脏页,写性能为0的概率变高


  • flush 一般是说刷脏页,
  • purge一般是指清undo log,
  • merge一般是指应用change buffer

在这里插入图片描述


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/193528.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RPA自动化办公07——Uibot流程加入python插件

参考:扩展UiBot命令_UiBot开发者指南 Uibot是非常方便,但是有些功能可能还需要别的语言来完成,例如python语言,作为胶水语言,在一个Uibot里面插入python脚本是很方便,好用的。 加入插件的位置 在新建一个流…

Seata分布式事务模式(TA、TCC、XA、SAGA)工作机制

前言 分布式应用痛点 分布式应用有一个比较明显的问题就是,一个业务流程通常需要几个服务来完成,业务的一致性很难保证。为了保障业务一致性,每一步都要在 catch 里去处理前面所有的“回滚”操作,可读性及维护性差,开…

分布式微服务

目录 认识微服务 单体服务架构的特点 分布式架构的特点 微服务 SpringCloud 总结 服务拆分及远程调用 服务拆分原则 假如在订单服务中,需要一起返回用户的信息 Eureka注册中心 Eureka的结构和作用 问题:order-service如何得知user-service实例…

基于拓扑的单树分割用于地面激光扫描点云的自动处理

Paper题目:Topology-based individual tree segmentation for automated processing of terrestrial laser scanning point clouds ABSTRACT 地面激光扫描 (TLS) 是一种基于地面的方法,可通过光探测和测距 (LiDAR) 技术快速获取 3D 点云。从 TLS 点云量…

搜索引擎关键字智能提示实践

为了提高阅读体验,请移步到:搜索引擎关键字智能提升实践一、背景搜索关键字智能提示是一个搜索应用的标配,主要作用是避免用户输入错误的搜索词,并将用户引导到相应的关键词上,以提升用户使用体验。雪球以连接人与资产…

【K8S之调度器流程和扩展】如何给 scheduler 添加扩展插件、关闭默认插件、创建多个 scheduler?

参考 自定义 Kubernetes 调度器 阳明https://github.com/cnych/sample-scheduler-extender kube-scheduler 源码位置 kubernetes 调度器的源码位于 kubernetes/pkg/scheduler 中,大体的代码目录结构如下所示:(不同的版本目录结构可能不太一样) kuber…

如何检测文章被搜索引擎收录(如何让搜索引擎收录网站)

如何写网站内容才利于搜索引擎收录 网站关键词要想有好的排名,网站本身必须是被搜索引擎收录的状态,另外,网站上的相关内容收录越多,搜索引擎给与网站关键词的排名靠前概率会越大,那么,网站内容怎样来写会…

[Linux]生产者消费者模型(基于BlockQueue的生产者消费者模型 | 基于环形队列的生产者消费者模型 | 信号量 )

文章目录生产者消费者模型函数调用角度理解生产者消费者模型生活角度理解生产者消费者模型为什么要使用生产者消费者模型生产者消费者模型优点321原则基于BlockingQueue的生产者消费者模型POSIX信号量回顾信号量概念信号量操作函数环形队列基于环形队列的生产者消费者模型生产者…

Android Studio如何打jar包和aar包并使用

Android Studio如何打jar包和使用生成jar包方式module方式生成jar方式第一类修改主app的方式第二类:通过新建module方式生成jar包如何使用jar包aar生成并使用aar生成aar使用之前有篇文章介绍了so库的生成和使用,看这里,但是,如果我…

[NOI Online #1 入门组] 文具订购

题目描述: 小明的班上共有 n 元班费,同学们准备使用班费集体购买 3 种物品: 圆规,每个 7 元。笔,每支 4 元。笔记本,每本 3 元。 小明负责订购文具,设圆规,笔,笔记本的订购数量分别…

Linux 下安装 JDK 和 Maven 环境

目录 1. 进入 maven 官网下载安装包 2. 安装 maven 3. 添加 Maven 环境变量 4. 配置 Maven 本地仓库 5. 配置镜像 6. 配置 JDK 7. 测试 操作系统:Centos 7.6 安装 maven 环境前,需要先安装 java 环境,笔者这里已经成功安装 java 环境&…

PHP基础知识 - PHP面向对象OOP

目录 一. 面向对象基本知识 1.1 面向对象概念 1.2 什么是类 1.3 什么是对象 1.4 类与对象的关系 1.5 PHP创建类的示例 二、类、属性、方法的修饰符 2.1 类的修饰符 2.2 成员方法的修饰符 2.3 成员属性修饰符 2.4 访问控制修饰符 2.5 static 静态修饰符 2.6 final…

多模块项目中,SpringBoot项目下启动失败-无法加载主类com.xch.XxxApplication

错误:项目启动时,无法找到主类(启动类)XxxApplication由于,主类需要先被编译,再被JVM找到编译后的文件运行如:XxxApplication.java-(编译)-XxxApplication.class-(运行)所以,原因:1、未编译情况…

基于微信小程序的校运会管理系统

末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端框架:VUE 数据库:MySQL5.7以上 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目:是 目录 一、项目简介…

Erlik 2:一个基于Flask开发的包含大量安全漏洞的研究平台

关于Erlik 2 Erlik 2,也被称为Vulnerable-Flask- App,该工具是一个基于Flask开发的包含大量安全漏洞的研究平台。本质上来说,它是一个包含了大量漏洞的Flask Web应用程序。有了这个实验环境,广大研究人员可以轻松在Web渗透测试领…

【算法题解】14. 有效的括号

文章目录题目解法:使用栈的特性Java 代码实现Go 代码实现复杂度分析这是一道 简单 题。 来自:leetcode 题目 给定一个只包括 (,),{,},[,] 的字符串 s ,判断字符串是否有效。 有效字…

哈工大操作系统学习笔记(二)进程与线程

文章目录CPU管理的直观想法多进程图像用户级线程内核级线程内核级线程实现操作系统之树CPU 调度策略一个实际的schedule 函数进程同步与信号量信号量临界区保护信号量的代码实现死锁处理CPU管理的直观想法 CPU的工作原理: 自动的取值执行,给了初始地址&…

Swift 周报 第二十一期

前言 本期是 Swift 编辑组自主整理周报的第十二期,每个模块已初步成型。各位读者如果有好的提议,欢迎在文末留言。 Swift 周报在 GitHub 开源,欢迎提交 issue,投稿或推荐内容。目前计划每两周周一发布,欢迎志同道合的…

永久删除的照片怎么找回来?教你三招恢复方法

如果文件被永久删除了,想要恢复就没有这么简单了,永久删除的文件可能是已经从回收站清空的文件,或者是我们按住快捷键“shiftdelete”快捷键删除的文件,这样的话,我们无法在电脑上面查找到文件,潜意识里面认…

SAP ABAP SY-REPID 变化「Note」

6.10 前(知悉) SY-CPROG The name of the calling program in an external routine, otherwise the name of the current program. 外部例程中调用程序的名称,否则为当前程序的名称 SY-REPID Name of the current ABAP program. For externa…