面试高频:为什么MySQL会抖一下?

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 大家好前面我们大概了解了如何巧妙的给字符串字段加索引提高查询性能。今天我们介绍一下为什么MySQL在查询数据的时候,有些时候会 "抖" 一下

先解释一下抖这个字。有些时候的SQL执行非常快,有些时候执行非常慢。通过explain查看SQL的执行计划还是无用。该走的索引也走了,该优化的细节也优了。那么到底是因为什么所以才导致卡顿一下的呢?

首先介绍一下什么是干净页跟脏页。先把路铺好。


干净页

内存上的数据和磁盘上的数据页的内容一致时,称为 “干净页”。

脏页

内存上的数据和磁盘上的数据页的内容不一致时,称为 “脏页”。

举个例子

举一个干净页跟脏页的例子,这个例子也是前几篇文章举的。一家农村型超市,这个超市没有大城市那种不赊账的规矩。所以农村的超市一般会涉及到赊账。那么如何进行有效的记账呢?我们不可能因为赊了一个人就记一次,那这样的话做事的效率是非常低的。因为每次在记账本上找到那个人的名字都要找很久。所以一般采用的是,会有一个临时纸条,这个纸条夹在一个固定的地方,每当晚上打样的时候,统一把所有纸条上的数据一一归到记账本上。

这样才是大多数超市的真实场景,那么数据库里这些是如何实现的呢?纸条就是redo log!记账本就是磁盘!

如下图所示

  1. 第一个小方格中的内容:当前赊账的数据还没有转移到记账本上。所以内存与磁盘上的数据不一致。这样就是一开始介绍的脏页。
  2. 第二个小方格中的内容:到了晚上或者不忙的时候,掌柜或者老板就把纸条上的数据转移到记账本上。这个过程在数据库中就是flush数据的过程。

综上所述:干净页,脏页大概就是这么一个东西。数据库之所以抖一下,就是因为数据库的内部正在进行flush操作

image.png

图1 超市赊账纸条转移到记账本的过程

那么数据库什么时候才会抖一下呢?什么时候开始flush呢?今天我们详细介绍一下


内存场景

我们介绍场景的时候,还是按照上述例子,先从简单易懂的真实场景,再到数据库抽象的过度!下面四个场景看不懂没关系,我们一一介绍。这篇文章还是比较偏底层介绍的,所以耐心一点,看完之后再回味一下。


纸条满了

第一种场景就是,所有纸条都记满了,这个时候必须放下手里的工作。把纸条上的所有账都转移到记账本上。对应的数据库过程就是。redo log满了。需要把redo log日志上的操作转移到磁盘中。变成真正的数据。


一张纸条满了

第二种场景就是,一张纸条满了,掌柜的不得不再找一张纸条过来,继续记账。对应的数据库流程就是,一页数据写满了,MySQL不得不再开一页进行存放数据。


纸条没满,不忙的时候

第三种场景就是,在超市低峰期的时候,都是比较悠闲的。所以这个时候老板闲着也是闲着,不如把纸条上的账,归到记账本上。对应的数据库流程就是,一段时间没人使用数据库的时候,数据库就会自己把redo log日志更新到磁盘上。


打样时

第四种场景就是,超市晚上打样时,完全没人。掌柜的收尾一天工作的时候把账归到记账本上。对应的数据库流程就是,用户关闭会话的时候。没有连接使用了。数据库也会把redo log更新到磁盘上。


分析

第三种情况与第四种情况都是属于空闲时,系统没什么压力。我们就不做分析了。这里着重分析一下第一种与第二钟情况。

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

一张纸条满了:“内存不够用了,要先将脏页写到磁盘”,这种情况其实是常态。InnoDB 用缓冲池(buffer pool)管理内存,缓冲池中的内存页有三种状态:还没有使用的,使用了并且是干净页,使用了并且是脏页。

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

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

所以刷脏页是数据库运行的常态了。那么下面两种情况会影响数据库的运行。也就是一开始我们说的数据库为什么会抖音一下的详细原因:

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

所以MySQL想用的稳定,用的爽就必须控制,刷脏页的时机。以及何时刷脏页,刷多块?


innodb刷脏页策略

这里解决一下何时刷脏页,刷多块的问题

首先,你要正确地告诉 InnoDB 所在主机的 IO 能力,这样 InnoDB 才能知道需要全力刷脏页的时候,可以刷多快。下列是数据库用到的参数,通过他来设置。怎么设置我们下面会介绍的。

show variables like 'innodb_io_capacity'

image.png

innodb_io_capacity 不能过高,也不能过低。过高的话会占用大量内存,会严重影响MySQL的日常使用。如果过低会导致脏页太多。数据跟不上使用。导致写入只能等。


如何设置

首先主要考虑两个因素。一个是脏页比例,一个是redo log写盘速度。

下列参数是控制脏页比例。默认值为75。也就是75%

show variables like 'innodb_max_dirty_pages_pct'

InnoDB 会根据当前的脏页比例(假设为 M),算出一个范围在 0 到 100 之间的数字

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 写入速度算出来的两个值。

image.png

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

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

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

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 中的一个机制,可能让你的查询会更慢:在准备刷一个脏页的时候,如果这个数据页旁边的数据页刚好是脏页,就会把这个“邻居”也带着一起刷掉;而且这个把“邻居”拖下水的逻辑还可以继续蔓延,也就是对于每个邻居数据页,如果跟它相邻的数据页也还是脏页的话,也会被放到一起刷。

紧接着相关联的参数是set innodb_flush_neighbors=1 允许连坐机制set innodb_flush_neighbors=0 不允许连坐机制

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

而如果使用的是 SSD 这类 IOPS 比较高的设备的话,我就建议你把 innodb_flush_neighbors 的值设置成 0。因为这时候 IOPS 往往不是瓶颈,而“只刷自己”,就能更快地执行完必要的刷脏页操作,减少 SQL 语句响应时间。

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

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
6天前
|
存储 关系型数据库 MySQL
【Java面试题汇总】MySQL数据库篇(2023版)
聚簇索引和非聚簇索引、索引的底层数据结构、B树和B+树、MySQL为什么不用红黑树而用B+树、数据库引擎有哪些、InnoDB的MVCC、乐观锁和悲观锁、ACID、事务隔离级别、MySQL主从同步、MySQL调优
【Java面试题汇总】MySQL数据库篇(2023版)
|
1月前
|
SQL 存储 关系型数据库
复盘女朋友面试4个月的Mysql面试题(1万字)
该文章详细分析了Ribbon的超时配置是否会覆盖OpenFeign的超时配置,并探讨了OpenFeign超时配置能否动态实时修改生效的问题。
复盘女朋友面试4个月的Mysql面试题(1万字)
|
1月前
|
关系型数据库 MySQL Java
面试官:说说MySQL调优?
面试官:说说MySQL调优?
62 5
面试官:说说MySQL调优?
|
1月前
|
SQL 关系型数据库 MySQL
面试准备-MySQL
面试准备-MySQL
|
1月前
|
缓存 NoSQL Redis
一天五道Java面试题----第九天(简述MySQL中索引类型对数据库的性能的影响--------->缓存雪崩、缓存穿透、缓存击穿)
这篇文章是关于Java面试中可能会遇到的五个问题,包括MySQL索引类型及其对数据库性能的影响、Redis的RDB和AOF持久化机制、Redis的过期键删除策略、Redis的单线程模型为何高效,以及缓存雪崩、缓存穿透和缓存击穿的概念及其解决方案。
|
1月前
|
算法 关系型数据库 MySQL
一天五道Java面试题----第七天(mysql索引结构,各自的优劣--------->事务的基本特性和隔离级别)
这篇文章是关于MySQL的面试题总结,包括索引结构的优劣、索引设计原则、MySQL锁的类型、执行计划的解读以及事务的基本特性和隔离级别。
|
30天前
|
存储 关系型数据库 MySQL
MySQL 常见面试题总结(上)
主要介绍 MYSQL 数据库面试中常见的面试问题。
17 0
|
30天前
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
|
30天前
|
XML 存储 JSON
【IO面试题 六】、 除了Java自带的序列化之外,你还了解哪些序列化工具?
除了Java自带的序列化,常见的序列化工具还包括JSON(如jackson、gson、fastjson)、Protobuf、Thrift和Avro,各具特点,适用于不同的应用场景和性能需求。
|
1月前
|
Java
【Java基础面试三十七】、说一说Java的异常机制
这篇文章介绍了Java异常机制的三个主要方面:异常处理(使用try、catch、finally语句)、抛出异常(使用throw和throws关键字)、以及异常跟踪栈(异常传播和程序终止时的栈信息输出)。