Innodb Buffer Pool缓存机制(四)预读与Mysql改进的LRU策略

一、什么是预读

InnoDB提供了预读(read ahead)。所谓预读,就是InnoDB认为执行当前的请求可能之后会读取某些页面,就预先把它们加载到Buffer Pool中。根据触发方式的不同,预读又可以细分为下边两种:

1.1 线性预读

InnoDB提供了一个系统变量innodb_read_ahead_threshold,如果顺序访问了某个区(extent)的页面超过这个系统变量的值,就会触发一次异步读取下一个区中全部的页面到Buffer Pool的请求。

这个innodb_read_ahead_threshold系统变量的值默认是56,我们可以在服务器启动时通过启动参数或者服务器运行过程中直接调整该系统变量的值,取值范围是0~64。

1.2 随机预读

如果Buffer Pool中已经缓存了某个区的13个连续的页面,不论这些页面是不是顺序读取的,都会触发一次异步读取本区中所有其他的页面到Buffer Pool的请求。InnoDB同时提供了innodb_random_read_ahead系统变量,它的默认值为OFF。

可以通过下面的命令查看:

show variables like '%_read_ahead%';

  如果预读到Buffer Pool中的页成功的被使用到,那就可以极大的提高语句执行的效率。如果用不到,这些预读的页都会放到LRU链表的头部,但是如果此时Buffer Pool的容量不太大而且很多预读的页面都没有用到的话,这就会导致处在LRU链表尾部的一些缓存页会很快的被淘汰掉,也就是所谓的劣币驱逐良币,会大大降低缓存命中率。

  加载到Buffer Pool中的页不一定被用到,如果非常多的使用频率偏低的页被同时加载到Buffer Pool时,可能会把那些使用频率非常高的页从Buffer Pool中淘汰掉。

因为有这两种情况的存在,所以InnoDB把这个LRU链表按照一定比例分成两截,分别是:

  1. 一部分存储使用频率非常高的缓存页,所以这一部分链表也叫做热数据,或者称young区域;
  2. 另一部分存储使用频率不是很高的缓存页,所以这一部分链表也叫做冷数据,或者称old区域;

大概结构如下图所示:
在这里插入图片描述
young区和old区的分界点并不是固定的,对于InnoDB存储引擎来说,可以通过查看系统变量innodb_old_blocks_pct的值来确定old区域在LRU链表中所占的比例:

SHOW VARIABLES LIKE 'innodb_old_blocks_pct';

在这里插入图片描述
从结果可以看出来,默认情况下,old区域在LRU链表中所占的比例是37%,也就是说old区域大约占LRU链表的3/8。这个比例我们是可以设置的,我们可以在启动时修改innodb_old_blocks_pct参数来控制old区域在LRU链表中所占的比例。

继续了解LRU改进策略推荐此文:【深度好文】Mysql 缓冲池(buffer pool)机制详解

二、全表扫描

扫描全表的查询语句(比如没有建立合适的索引或者压根儿没有WHERE子句的查询)意味着将访问该表所在的所有页!

假设这个表中记录非常多的话,那该表会占用特别多的页,当需要访问这些页时,会把它们统统都加载到Buffer Pool中,这也就意味着Buffer Pool中的所有页都被换了一次血,其他查询语句在执行时又得执行一次从磁盘加载到Buffer Pool的操作。而这种全表扫描的语句执行的频率也不高,每次执行都要把Buffer Pool中的缓存页换一次血,这严重的影响到其他查询对Buffer Pool的使用,从而大大降低了缓存命中率。

如果非常多的使用频率偏低的页被同时加载到Buffer Pool时,可能会把那些使用频率非常高的页从Buffer Pool中淘汰掉。

对于这两种情况,InnoDB规定,当磁盘上的某个页面在初次加载到Buffer Pool中的某个缓存页时,该缓存页对应的控制块会被放到old区域的头部。这样针对预读到Buffer Pool却不进行后续访问的页面就会被逐渐从old区域逐出,而不会影响young区域中被使用比较频繁的缓存页。

在进行全表扫描时,虽然首次被加载到Buffer Pool的页被放到了old区域的头部,但是后续会被马上访问到,每次进行访问的时候又会把该页放到young区域的头部,这样仍然会把那些使用频率比较高的页面给顶下去。

但全表扫描有一个特点,那就是它的执行频率非常低,出现了全表扫描的语句也是我们应该尽快优化的对象。而且在执行全表扫描的过程中,即使某个页面中有很多条记录需要访问,也就是去多次访问这个页面所花费的时间也是非常少的。

所以在对某个处在old区域的缓存页进行第一次访问时就在它对应的控制块中记录下来这个访问时间,如果后续的访问时间与第一次访问的时间在某个时间间隔内,那么该页面就不会被从old区域移动到young区域的头部,否则将它移动到young区域的头部。上述的这个间隔时间是由系统变量innodb_old_blocks_time控制的:

SHOW VARIABLES LIKE 'innodb_old_blocks_time';

这个innodb_old_blocks_time的默认值是1000,它的单位是毫秒,也就意味着对于从磁盘上被加载到LRU链表的old区域的某个页来说,如果第一次和最后一次访问该页面的时间间隔小于1s(很明显在一次全表扫描的过程中,多次访问一个页面中的时间不会超过1s),那么该页是不会被加入到young区域的, 当然,像innodb_old_blocks_pct一样,我们也可以在服务器启动或运行时设置innodb_old_blocks_time的值,这里需要注意的是,如果我们把innodb_old_blocks_time的值设置为0,那么每次我们访问一个页面时就会把该页面放到young区域的头部。

三、脏页刷新

InnoDB有专门的后台线程每隔一段时间负责把脏页刷新到磁盘,这样可以不影响用户线程处理正常的请求。主要有两种刷新路径:

  • 从LRU链表的冷数据中刷新一部分页面到磁盘;

后台线程会定时从LRU链表尾部开始扫描一些页面,扫描的页面数量可以通过系统变量innodb_lru_scan_depth来指定,如果从里边儿发现脏页,会把它们刷新到磁盘。这种刷新页面的方式被称之为BUF_FLUSH_LRU。

  • 从flush链表中刷新一部分页面到磁盘;

后台线程也会定时从flush链表中刷新一部分页面到磁盘,刷新的速率取决于当时系统是不是很繁忙。这种刷新页面的方式被称之为BUF_FLUSH_LIST。

有时候后台线程刷新脏页的进度比较慢,导致用户线程在准备加载一个磁盘页到Buffer Pool时没有可用的缓存页,这时就会尝试看看LRU链表尾部有没有可以直接释放掉的未修改页面,如果没有的话会不得不将LRU链表尾部的一个脏页同步刷新到磁盘(和磁盘交互是很慢的,这会降低处理用户请求的速度)。这种刷新单个页面到磁盘中的刷新方式被称之为BUF_FLUSH_SINGLE_PAGE

四、多个Buffer Pool实例

Buffer Pool本质是InnoDB向操作系统申请的一块连续的内存空间,在多线程环境下,访问Buffer Pool中的各种链表都需要加锁处理,在Buffer Pool特别大而且多线程并发访问特别高的情况下,单一的Buffer Pool可能会影响请求的处理速度。

所以在Buffer Pool特别大的时候,我们可以把它们拆分成若干个小的Buffer Pool,每个Buffer Pool都称为一个实例,它们都是独立的,独立的去申请内存空间,独立的管理各种链表,所以在多线程并发访问时并不会相互影响,从而提高并发处理能力。

可以在服务器启动的时候通过设置innodb_buffer_pool_instances的值来修改Buffer Pool实例的个数。

每个Buffer Pool实例实际占内存空间 = innodb_buffer_pool_size / innodb_buffer_pool_instances

也不是说Buffer Pool实例创建的越多越好,分别管理各个Buffer Pool也是需要性能开销的。

InnoDB规定:innodb_buffer_pool_instances能设置的最大值是64,而且当innodb_buffer_pool_size(默认128M)的值小于1G的时候设置多个实例是无效的,InnoDB会默认把innodb_buffer_pool_instances的值修改为1。

按照官方的说明,最佳的innodb_buffer_pool_instances的数量是,innodb_buffer_pool_size除以innodb_buffer_pool_instances,可以让每个Buffer Pool实例达到1个G,这个公式在8.0和5.7中都适用。

五、动态修改Buffer Pool大小

在MySQL 5.7.5之前,Buffer Pool的大小只能在服务器启动时通过配置innodb_buffer_pool_size启动参数来调整大小,在服务器运行过程中是不允许调整该值的。不过MySQL在5.7.5以及之后的版本中支持了在服务器运行过程中调整Buffer Pool大小的功能。

但是有一个问题,就是每次当我们要重新调整Buffer Pool大小时,都需要重新向操作系统申请一块连续的内存空间,然后将旧的Buffer Pool中的内容复制到这一块新空间,这是极其耗时的。

所以MySQL决定不再一次性为某个Buffer Pool实例向操作系统申请一大片连续的内存空间,而是以一个所谓的chunk为单位向操作系统申请空间。

也就是说一个Buffer Pool实例其实是由若干个chunk组成的,一个chunk就代表一片连续的内存空间,里边儿包含了若干缓存页与其对应的控制块:
在这里插入图片描述
正是有了chunk的概念,在服务器运行期间调整Buffer Pool的大小时就是以chunk为单位增加或者删除内存空间,而不需要重新向操作系统申请一片大的内存,然后进行缓存页的复制。

这个所谓的chunk的大小是我们在启动操作MySQL服务器时通过innodb_buffer_pool_chunk_size启动参数指定的,它的默认值是134217728,也就是128M。不过需要注意的是,innodb_buffer_pool_chunk_size的值只能在服务器启动时指定,在服务器运行过程中是不可以修改的。

Buffer Pool的缓存页除了用来缓存磁盘上的页面以外,还可以存储锁信息自适应哈希索引等信息。

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

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

相关文章

掘金AI商战宝典-高阶班:如何用AI制作视频(11节视频课)

课程下载:掘金AI商战宝典-高阶班:如何用AI制作视频(11节视频课)-课程网盘链接提取码下载.txt资源-CSDN文库 更多资源下载:关注我。 课程目录: 1-第一讲用AI自动做视频(上)_1.mp4 2-第二讲用AI自动做视频(中)_1.mp4 3-第四讲A…

U9C的数据查询视图Sql

U9C的数据查询视图Sql if object_id(TEMPDB..#priceTable) is not null begin drop table #priceTable endcreate table #priceTable (polineCreatedOn date,price varchar(max),itemid varchar(max),purchaseOrderdocno varchar(max),)insert into #priceTable select max(…

阿里云邮件推送服务配置教程:怎么做批发?

阿里云邮件推送的API配置步骤?配置教程有哪些步骤? 阿里云邮件推送服务凭借其高并发、稳定性强和安全性高等特点,成为众多企业的首选。Aok将详细介绍如何使用阿里云邮件推送服务进行批发配置,并简要提及AokSend的优势。 阿里云邮…

UE4_环境_材质函数

学习笔记,不喜勿喷,欢迎指正,侵权立删! 1、建立材质函数Distance_Fun,勾选公开到库。 2、添加函数输入节点FunctionInput, 这个输入我们想作为混合材质属性BlendMaterialAttributes的alpha输入节点&#x…

022、键管理_遍历键

Redis提供了两个命令遍历所有的键,分别是keys和scan 1.全量遍历键 keys patternkeys命令是支持pattern匹配的 127.0.0.1:6379> dbsize (integer) 0 127.0.0.1:6379> mset hello world redis best jedis best hill high OK如果要获取所有的键,可以使用keys pattern命…

手撸 串口交互命令行 及 AT应用层协议解析框架

在嵌入式系统开发中,命令行接口(CLI)和AT命令解析是常见的需求。CLI提供了方便的调试接口,而AT命令则常用于模块间的通信控制。本文将介绍如何手动实现一个串口交互的命令行及AT应用层协议解析框架,适用于FreeRTOS系统…

06Docker-Compose和微服务部署

Docker-Compose 概述 Docker Compose通过一个单独的docker-compose.yml模板文件来定义一组相关联的应用容器,帮助我们实现多个相互关联的Docker容器的快速部署 一般一个docker-compose.yml对应完整的项目,项目中的服务和中间件对应不同的容器 Compose文件实质就…

锂电池寿命预测 | Matlab基于SSA-SVR麻雀优化支持向量回归的锂离子电池剩余寿命预测

目录 预测效果基本介绍程序设计参考资料 预测效果 基本介绍 【锂电池剩余寿命RUL预测案例】 锂电池寿命预测 | Matlab基于SSA-SVR麻雀优化支持向量回归的锂离子电池剩余寿命预测(完整源码和数据) 1、提取NASA数据集的电池容量,以历史容量作…

【C++课程学习】:类和对象(上)(类的基础详细讲解)

🎁个人主页:我们的五年 🔍系列专栏:C课程学习 🎉欢迎大家点赞👍评论📝收藏⭐文章 目录 🍟1.1类的引出: 🍟1.2类的结构: 🍟1.3类的…

LeetCode-82. 删除排序链表中的重复元素 II【链表 双指针】

LeetCode-82. 删除排序链表中的重复元素 II【链表 双指针】 题目描述:解题思路一:用一个cur即可实现去重cur.next cur.next.next背诵版:解题思路三:0 题目描述: 给定一个已排序的链表的头 head , 删除原始…

【java前端课堂】02_类和方法的定义区别

目录 简介: 类 方法 类和方法之间的主要区别如下: 定义与结构: 实例化: 作用范围: 生命周期: 下面是一个简单的Java类和方法示例: 简介: 类 在Java(以及许多其他面向…

十大排序-冒泡排序

算法原理如下: 给出一组数据;比较相邻的元素。如果第一个比第二个大,互换两个值。对每一组相邻元素同样方式比较,从开始的第一组到结束的最后一组。最后的元素会是最大数。除了排列好的最大数,针对所有元素重复以上步…

台式机ubuntu22.04安装nvidia驱动

总结一个极简易的安装方法 正常安装ubuntu 22.04正常更新软件 sudo apt update sudo apt upgrade -y参考ubuntu官方网站的说明https://ubuntu.com/server/docs/nvidia-drivers-installation#/ # 首先检查系统支持驱动的版本号 sudo ubuntu-drivers list我显示的内容如下&…

前端应用开发实验:组件应用

目录 实验目的相关知识点实验内容及要求代码实现效果 实验目的 (1)掌握组件的创建方法(全局组件、局部组件); (2)重点学会组件之间的数据传递(prop传值、自定义事件)&am…

SAP 用事务码SQVI 制作简单的ALV报表

我们在项目实施和运维的过程中经常会接到用户的很多需求,有很大的一部分需求可能都是一些报表的需求,有些报表的需求需要开发人员使用ABAP编写,但是有些报表仅仅只是两个或者多个报表的表关联就可以实现。这个时候我们就可以用SQVI这个事物代…

揭秘!宠物空气净化器对抗猫毛过敏,效果真的超乎想象?

猫毛过敏困扰着不少爱猫人士。尽管网络上充斥着各种缓解策略,但究竟哪种方法效果最佳?作为一位经验丰富的宠物主人,我搜集了大量信息,对比了几种主流的猫毛过敏应对策略,比如药物治疗、日常清洁和宠物空气净化器的使用…

阿里云私有CA使用教程

点击免费生成 根CA详情 启用根CA -----BEGIN CERTIFICATE----- MIIDpzCCAogAwIBAgISBZ2QPcfDqvfI8fqoPkOq6AoMA0GCSqGSIb3DQEBCwUA MFwxCzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdiZWlqaW5nMRAwDgYDVQQHDAdiZWlq aW5nMQ0wCwYDVQQKDARDU0REMQ0wCwYDVQQLDARDU0REMQswCQYDVQQDDAJDTjA…

单列集合--ArryList、LinkedList、Set

使用IDEA进入某个类之后,按ctrlF12,或者alt数字7,可查看该实现类的大纲。 package exercise;import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.function.Consumer;public class Demo3 {public static void…

开放式耳机哪个牌子好?2024年度热门机型推荐榜单分享!

随着音乐技术的不断革新,开放式耳机已成为音乐发烧友们的首选。从最初的简单音质,到如今的高清解析,开放式耳机不断进化。音质纯净,佩戴舒适,无论是街头漫步还是家中细细静听,都能带给你身临其境的音乐体验…

iOS18 新变化提前了解,除了AI还有这些变化

iOS 18即将在不久的将来与广大iPhone用户见面,这次更新被普遍认为是苹果历史上最重要的软件更新之一。据多方报道和泄露的消息,iOS 18将带来一系列全新的功能和改进,包括在人工智能领域的重大突破、全新的设计元素以及增强的性能和安全性。现…