Mysql专栏 - 缓冲池的内部结构(二)

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
简介: Mysql专栏 - 缓冲池的内部结构(二)

Mysql专栏 - 缓冲池的内部结构(二)

前言

这是mysql专栏的第四篇,上一个小节我们了解了如何通过flush list存储所有的脏页数据,这一节我们来继续介绍缓冲池的内部结构LRU链表。

概述

缓冲池的大小是固定的,缓冲池当然不是永远都驻留在缓冲池的,但是空闲缓冲页不够情况下如何处理呢?本节将会讨论缓冲池重要的淘汰机制:LRU的淘汰机制,后续会介绍mysql的冷热数据分离特性,最后将给出几个思考题回顾整个内容。

缓存页的刷新机制 - LRU淘汰缓存页

Buffer pool 中的缓存页不够怎么办?

经过上一节的讨论,当执行器发来了增删改查的请求的时候会从磁盘文件读取对应的数据块到缓冲池当中,之前提到过缓冲池不是无限的,默认情况下最多只有128m,一旦所有的缓存页都被加载就意味着free list内部没有空闲的缓存页,当所有的空闲缓存页被分配完了,这意味着缓冲池已经无法再分配缓冲页了,但是我们还想把数据页加载到缓存池怎么办?

如果我们想要加载新的缓存页也十分简单,只要淘汰一些不常用的缓存页即可。

淘汰那个缓存,淘汰谁?

淘汰缓冲页就是把缓冲池里面的某个缓冲页刷新到磁盘(必须先刷新数据到磁盘)然后把对应的缓存页删除即可。接着再把新的数据页的内容加载到缓冲池即可。那么究竟要把那个缓存页刷新到磁盘呢?

缓存命中率

缓存命中率很好理解,假设有两个缓存页,第一个缓存页在100次请求中查询和修改了30次,意味着这个缓存页的命中率为30%。并且缓存命中率不错。第二个缓存页则在100次内只操作了1,2次,这意味着缓存命中率很低,所以不用说,肯定是淘汰第二个。

LRU链表淘汰算法

为了判断哪些缓存页经常被访问,哪些缓存页很少被访问。mysql引入一个新的LRU链表,LRU 就是least recently used,也就是最少使用的意思。通过这个LRU链表,我们就可以知道那个缓存页是最少使用的,当需要一个新的缓存页的时候就可以通过一个LRU链表知道那个缓存页使用频率最低并将其刷新到磁盘文件并且移除。

当某个缓存页被操作的时候,就会找到LRU列表对应的节点加入进去,需要淘汰一个缓存页,就找到LRU列表的尾部进行淘汰(输入磁盘并且从缓冲池情况,同时free list增加一个空闲的描述信息节点),因为最后一个节点肯定是使用频率最低的。

下面我们根据之前文章的结构图,补充一个LRU链表,最后的结构图内容如下:

image.png

简单的LRU链表存在哪些问题?

当Free list没有可用的空闲节点的时候,需要从LRU链表的尾部刷新一个缓存块到磁盘并且清空这个缓存块把位置让给新的数据块。

但是mysql的LRU的链表有许多的特性。那么在介绍新特性之前,我们来看下普通的LRU链表会带来哪些问题

简单的LRU链表有哪些问题呢?

1. 预读:

首先这样的LRU有一个重大的隐患:预读,比如现在存在两个空闲缓存页,加载一个数据页之后,同时会把相邻的数据页页加载到缓存区,正好每一个数据页放入一个空闲缓存页。意味着实际上只有一个缓存页被访问了,另一通过预读的机制加载的缓存页,但是这两个都被放到了链表的最前面,最后,预读会造成尾端的缓存页被错误的删除,然而正确的做法是删除第二个被预读缓存的缓存页

接着我们来看看,到底哪些情况下会触发MySQL的预读机制呢?

(1) 有一个参数是**innodb_read_ahead_threshold**,他的默认值是56,意思就是如果顺序的访问了一个区里的多个数据页,访问的数据页的数量超过了这个阈值,此时就会触发预读机制,把下一个相邻区中的所有数据页都加载到缓存里去。
(2) 如果Buffer Pool里缓存了一个区里的13个连续的数据页,而且这些数据页都是比较频繁会被访问的,此时就会直接触发预读机制,把这个区里的其他的数据页都加载到缓存里去
这个预读机制是通过参数**innodb_random_read_ahead**来控制的,他**默认是OFF**,也就是这个规则默认是关闭的

吐槽:预读的机制有点类似机器磁盘的顺序访问操作。

2. 全表扫描

全表扫描相信学过数据库的都知道这个理念,。从底层来看一个全表扫描的查询可能会把表所有的数据页放到buffer pool里面,最终可能会把一整个表的数据页加载到缓存页里面,LRU的前面一大串页都是全表查询的数据页。这会导致尾部淘汰的缓存页是一些经常用到的缓存页,而留下的都是不怎么使用的数据块,这样缓存的命中率会大大降低,导致整个mysql的性能十分差。

冷热数据分离的LRU

解决上面的两个问题激素使用冷热分离的LRU,冷热分离的意思是说按照一定的比例把整个链表分为热数据和冷数据,mysql当中由 innodb_old_blocks_pct 参数进行控制,默认是37, 意味着冷数据占了37%,热数据占了63%

冷热数据如何使用

第一次加载的时候缓存页的数据会放到哪一个位置?稍微琢磨一下不难得出答案那就是:冷数据的头部。第一次把数据页加入到缓存页默认会放到冷数据的头部。

冷数据什么时候进入热数据

冷数据进入热数据肯定是需要一定的缓存命中率的,所以是按照缓存命中率判定的,是这样么?其实不是的这样想是错的,因为这很难作为一个权衡条件。其实冷热数据是按照第一次加载缓冲页1S之后如果你还是访问了这个数据页,那么这个数据就会升级为热数据也就是放到热数据的头部,另外这个参数是根据innodb_old_blocks_time这个参数进行判断的,默认设置的参数就1000(毫秒)也就是1s。

image.png

缓存页不够如何淘汰缓存

冷热分离之后淘汰缓存页就简单了,直接找到冷数据的尾部缓存页,把这些缓存文件刷到磁盘文件之后可以直接清除,无需担心他们这些数据可能是频繁访问的数据。

冷热分离如何解决预读和全表查询问题

当预读和全表查询加载出一大堆的数据之后,会发现他们的数据其实都在冷数据的头部的,但是如果1S之后依然频繁访问的冷数据,则会不断的放到热数据的头部去的,但是一大段读取出来的冷数据,由于只访问了一次之后就再也没有访问过了。所以是没有什么关系的。

预读和全表加载的数据,会进入热数据区域么?

如果仅仅是一个全表扫描的查询,此时你肯定是在1s内就把一大堆缓存页加载进来,然后就访问了这些缓存页一下后就完事了,通常这些操作1s内就结束了。也就是说一个全表查的数据许多的临时数据是会直接放到冷数据页的。但是如果这部分数据在1S之后再次被访问,才会升级为热数据。但是“全表查最好尽量避免,错误的热数据也是隐患”。

总结:

到现在为止我们已经彻底搞定了LRU链表的设计机制,刚加载数据的缓存页都是放冷数据区域的头部的,而1s过后被访问了才会放热数据区域的头部,热数据区域的缓存页被访问了,就会自动放到头部去。这样的话实际上冷数据区域放的都是加载进来的缓存页,最多在1s内被访问过,之后就再也没访问过的冷数据缓存页!而加载进来之后在1s过后还经常被访问的缓存页都放在了热数据区域里,他们进行了冷热数据的隔离!这样的话在淘汰缓存的时候,一定是优先淘汰冷数据区域几乎不怎么被访问的缓存页的,最后这种冷热数据分离的思想是十分值得借鉴的一种设计思想。

思考题:

为什么MySQL要设计预读这个机制?

为什么MySQL要设计预读这个机制? 他加载一个数据页到缓存里去的时候,为什么要把一些相邻的数据页也加载到缓存里去呢?这么做的意义在哪里? 是为了应对什么样的一个场景?

为了优化性能引入了预读的机制,顺序读取之后可能会出现后续的顺序读取,所以加载后面的数据页也是合理的,但是理想情况下这种预读可能是好心办坏事,一旦这些预读的页没有加载出来,就是在捣乱了。所以这也是为什么mysql默认情况下是这个规则关闭的(设计的确实不太好)

为什么要设置1S的规则

其实这个规则是针对 全表查询而设置的,因为全表查询会一次性加载出很多的数据页到缓冲池,但是这些数据在短时间可能被误判为热数据,设置1S是因为大部分的全表查询基本都能在1S内完成(当然海量数据除外)。

redis的冷热数据问题

对于这种缓存中同时包含冷热数据的场景,如果你是在Redis中你的业务系统放了很多缓存数据,其中也是冷热数据都有的,此时可能会有什么问题?那么针对这样的一个问题,你是否可以考虑在你自己的缓存设计中,运用冷热隔离的思想来优化重构呢?

肯定是存在问题的,因为假设我们有1亿个商品,然后查询商品不在缓存里面就放到缓存里面,大量不经常访问的数据会在redis里面占用的很多内存但是没有人访问。所以这时候热数据的预加载就会用上的了,统计哪些商品访问的次数最多。然后晚上启动定时任务,把热数据放到redis里面,第二天加载的时候就会优先加载热数据了。

写在最后

如果觉得有帮助希望不忘点个赞给予支持,你的支持和鼓励是我最大的动力,最后欢迎关注个人微信公众号:懒时小窝


相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
6月前
|
存储 缓存 关系型数据库
MySQL的varchar水真的太深了——InnoDB记录存储结构
varchar(M) 能存多少个字符,为什么提示最大16383?innodb怎么知道varchar真正有多长?记录为NULL,innodb如何处理?某个列数据占用的字节数非常多怎么办?影响每行实际可用空间的因素有哪些?本篇围绕innodb默认行格式dynamic来说说原理。
949 6
MySQL的varchar水真的太深了——InnoDB记录存储结构
|
6月前
|
存储 JSON 关系型数据库
轻松入门MySQL:MySQL字段类型精解,优化存储结构,助力系统高效运行(2)
轻松入门MySQL:MySQL字段类型精解,优化存储结构,助力系统高效运行(2)
|
6月前
|
存储 关系型数据库 MySQL
MySQL InnoDB数据存储结构
MySQL InnoDB数据存储结构
|
17天前
|
存储 关系型数据库 MySQL
阿里面试:为什么要索引?什么是MySQL索引?底层结构是什么?
尼恩是一位资深架构师,他在自己的读者交流群中分享了关于MySQL索引的重要知识点。索引是帮助MySQL高效获取数据的数据结构,主要作用包括显著提升查询速度、降低磁盘I/O次数、优化排序与分组操作以及提升复杂查询的性能。MySQL支持多种索引类型,如主键索引、唯一索引、普通索引、全文索引和空间数据索引。索引的底层数据结构主要是B+树,它能够有效支持范围查询和顺序遍历,同时保持高效的插入、删除和查找性能。尼恩还强调了索引的优缺点,并提供了多个面试题及其解答,帮助读者在面试中脱颖而出。相关资料可在公众号【技术自由圈】获取。
|
9天前
|
JSON 关系型数据库 MySQL
MySQL JSON数据存储结构与操作
通过本文的介绍,我们了解了MySQL中JSON数据类型的基本操作、常用JSON函数、以及如何通过索引和优化来提高查询性能。JSON数据类型为存储和操作结构化数据提供了灵活性和便利性,在现代数据库应用中具有广泛的应用前景。希望本文对您在MySQL中使用JSON数据类型有所帮助。
22 0
|
4月前
|
SQL 关系型数据库 MySQL
Mysql:如何自定义导出表结构
通过以上方法,你可以灵活地自定义导出MySQL中的表结构,以满足不同的需求和场景。在进行操作的时候要注意权限问题以及路径问题,确保MySQL用户有权限写入指定的文件路径。在执行导出任务之前,还应确保你对数据库及其内容有足够的了解,以避免不必要的数据丢失或损坏。
64 1
|
4月前
|
SQL 关系型数据库 MySQL
实时计算 Flink版产品使用问题之要将MySQL同步到Doris,并设置整库同步,只变更库名、表名和表结构都不变,该如何设置
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
5月前
|
存储 关系型数据库 MySQL
|
5月前
|
SQL 关系型数据库 MySQL
mysqldiff - Golang 针对 MySQL 数据库表结构的差异 SQL 工具
Golang 针对 MySQL 数据库表结构的差异 SQL 工具。https://github.com/camry/mysqldiff
92 7
|
5月前
|
存储 算法 关系型数据库
【MySQL技术内幕】4.4-InnoDB数据页结构
【MySQL技术内幕】4.4-InnoDB数据页结构
106 1