1. 回顾

上一篇我们主要讲了InnoDB的存储引擎,其中主要的一个组件就是缓存池Buffer Pool,缓存了磁盘的真实数据,然后基于缓存做增删改查操作,同时配合了后续的redo log、刷磁盘等机制和操作。如下图:



这一篇,深入该组件内部,学习一下其设计思想。

2. Buffer Pool数据结构

Buffer Pool本质其实就是数据库的一个内存组件,默认情况下是128MB,如果我们的数据库如果是16核32G的机器,那么你就可以给Buffer Pool分配个2GB的内存,使用下面的配置就可以了

innodb_buffer_pool_size = 2147483648

磁盘加载数据页到缓存,数据页在缓存中被定义为缓存页,缓存页与缓存页默认16KB,每个缓存页有对应的描述数据,描述了这个数据页所属的表空间、数据页的编号、这个缓存页在Buffer Pool中的地址等。在Buffer Pool中,每个缓存页的描述数据放在最前面,各个缓存页放在后面。Buffer Pool中的描述数据大概相当于缓存页大小的5%左右,也就是每个描述数据大概是800个字节左右的大小,然后假设你设置的buffer pool大小是128MB,实际上Buffer Pool真正的最终大小会超出一些,可能有个130多MB的样子,因为他里面还要存放每个缓存页的描述数据。

数据结构如下图:

3. free链表

接着我们来看下一个问题,当数据库运行起来之后,肯定会不停的执行增删改查的操作,此时就需要不停的从磁盘上读取一个一个的数据页放入Buffer Pool中的对应的缓存页里去,把数据缓存起来,那么以后就可以对这个数据在内存里执行增删改查了。但是此时在从磁盘上读取数据页放入Buffer Pool中的缓存页的时候,必然涉及到一个问题,就是哪些缓存页是空闲的?

所以数据库会为Buffer Pool设计一个free链表,他是一个双向链表数据结构,这个free链表里,每个节点就是一个空闲的缓存页的描述数据块的地址,也就是说,只要你一个缓存页是空闲的,那么他的描述数据块就会被放入这个free链表中。如果要把数据页写入缓存页,就从链表中摘除这个节点,将该节点的描述数据写到Buffer pool,再把写入对应的缓存页。

写入之前还会判断该数据页是否已经被缓存,引入哈希表数据结构,他会用表空间号+数据页号,作为一个key,然后缓存页的地址作为value,当要使用一个数据页的时候,通过“表空间号+数据页号”作为key去这个哈希表里查一下,如果没有就读取数据页,如果已经有了,就说明数据页已经被缓存了。

如下图所示:

4. flush链表

更新过的缓存页与磁盘不一致,需要刷到磁盘的缓冲页构成的双向链表;也叫待刷盘的脏页数据页链表;如下图所示

5. lru链表

如果所有的缓存页都被塞了数据了,此时无法从磁盘上加载新的数据页到缓存页里去了,那么此时你只有一个办法,就是淘汰掉一些缓存页。引入LRU链表来判断哪些缓存页是不常用的。这个所谓的LRU就是Least Recently Used,最近最少使用的意思。

假设某个缓存页的描述数据块本来在LRU链表的尾部,后续你只要查询或者修改了这个缓存页的数据,也要把这个缓存页挪动到LRU链表的头部去,也就是说最近被访问过的缓存页,一定在LRU链表的头部。如下图所示

淘汰不常用的缓存页,尾部淘汰冷数据,头部插入热数据。

6. 数据页预读带来的问题

MySQL为提升读取性能,引入了预读机制,就是当从磁盘上加载一个数据页的时候,他可能会连带着把这个数据页相邻的其他数据页,也加载到缓存里去!

举个例子,假设现在有两个空闲缓存页,然后在加载一个数据页的时候,连带着把他的一个相邻的数据页也加载到缓存里去了,正好每个数据页放入一个空闲缓存页!但是接下来呢,实际上只有一个缓存页是被访问了,另外一个通过预读机制加载的缓存页,其实并没有人访问,此时这两个缓存页可都在LRU链表的前面。

我们可以看到,这个图里很清晰的表明了,前两个缓存页都是刚加载进来的,但是此时第二个缓存页是通过预读机制捎带着加载进来的,他也放到了链表的前面,但是他实际没人访问他。除了第二个缓存页之外,第一个缓存页,以及尾巴上两个缓存页,都是一直有人访问的那种缓存页,只不过上图代表的是刚刚把头部两个缓存页加载进来的时候的一个LRU链表当时的情况。

哪些场景会导致预读呢?

(1)有一个参数是innodb_read_ahead_threshold,他的默认值是56,意思就是如果顺序的访问了一个区里的多个数据页,访问的数据页的数量超过了这个阈值,此时就会触发预读机制,把下一个相邻区中的所有数据页都加载到缓存里去。

(2)如果Buffer Pool里缓存了一个区里的13个连续的数据页,而且这些数据页都是比较频繁会被访问的,此时就会直接触发预读机制,把这个区里的其他的数据页都加载到缓存里去。

这个机制是通过参数innodb_random_read_ahead来控制的,他默认是OFF,也就是这个规则是关闭的。

(3)接着我们讲另外一种可能导致频繁被访问的缓存页被淘汰的场景,那就是全表扫描。

这个所谓的全表扫描,意思就是类似如下的SQL语句:SELECT * FROM USERS,此时他没加任何一个where条件,会导致他直接一下子把这个表里所有的数据页,都从磁盘加载到Buffer Pool里去。这个时候他可能会一下子就把这个表的所有数据页都一一装入各个缓存页里去!此时可能LRU链表中排在前面的一大串缓存页,都是全表扫描加载进来的缓存页!那么如果这次全表扫描过后,后续几乎没用到这个表里的数据呢?此时LRU链表的尾部,可能全部都是之前一直被频繁访问的那些缓存页!然后当你要淘汰掉一些缓存页腾出空间的时候,就会把LRU链表尾部一直被频繁访问的缓存页给淘汰掉了,而留下了之前全表扫描加载进来的大量的不经常访问的缓存页。

7. 解决预读带来的问题

所以为了解决上一讲我们说的简单的LRU链表的问题,真正MySQL在设计LRU链表的时候,采取的实际上是冷热数据分离的思想。

所以真正的LRU链表,会被拆分为两个部分,一部分是热数据,一部分是冷数据,这个冷热数据的比例是由innodb_old_blocks_pct参数控制的,他默认是37,也就是说冷数据占比37%。



第一次被加载了数据的缓存页,都会不停的移动到冷数据区域的链表头部。冷数据区域的缓存页什么时候会放到热数据区域呢?实际上肯定很多人会想,只要对冷数据区域的缓存页进行了一次访问,就立马把这个缓存页放到热数据区域的头部行不行呢?

其实这也是不合理的,如果你刚加载了一个数据页到那个缓存页,他是在冷数据区域的链表头部,然后立马(在1ms以内)就访问了一下这个缓存页,之后就再也不访问他了呢?难道这种情况你也要把那个缓存页放到热数据区域的头部吗?

所以MySQL设定了一个规则,他设计了一个innodb_old_blocks_time参数,默认值1000,也就是1000毫秒。也就是说,必须是一个数据页被加载到缓存页之后,在1s之后,你访问这个缓存页,他才会被挪动到热数据区域的链表头部去。因为假设你加载了一个数据页到缓存去,然后过了1s之后你还访问了这个缓存页,说明你后续很可能会经常要访问它,这个时间限制就是1s,因此只有1s后你访问了这个缓存页,他才会给你把缓存页放到热数据区域的链表头部去。

该思想通过冷热分离+时间访问限制,解决了误淘汰热数据的问题。吸收冷热隔离思想,结合项目场景,可以优化缓存中的冷热数据。

LRU链表的热数据区域是如何进行优化的呢?

热数据区域里的缓存页可能是经常被访问的,所以这么频繁的进行移动是不是性能也并不是太好?也没这个必要。

所以说,LRU链表的热数据区域的访问规则被优化了一下,即你只有在热数据区域的后3/4部分的缓存页被访问了,才会给你移动到链表头部去。如果你是热数据区域的前面1/4的缓存页被访问,他是不会移动到链表头部去的。举个例子,假设热数据区域的链表里有100个缓存页,那么排在前面的25个缓存页,他即使被访问了,也不会移动到链表头部去的。但是对于排在后面的75个缓存页,他只要被访问,就会移动到链表头部去。这样的话,他就可以尽可能的减少链表中的节点移动了。

8. 总结

本节主要讲了三链表的作用,free链表记录空闲缓存页,flush链表记录脏页,即待刷盘缓存页,当free链表没有空闲时,lru链表淘汰最近不常用的缓存页。三链表动态执行过程可以表述为:free链表移除结点,lru链表冷数据区头部加入该节点;如果修改了缓存页,flush加入这个脏页,lru表中还可能会从冷数据区域移动到热数据区域的头部去;如果查询了缓存页,会把这个缓存页在lru链表中移动到热数据区域去,或者在热数据区域中也有可能会移动到头部去。总之,要么free链表移除节点,flush链表加节点,lru链表移动节点;要么free加节点,flush减节点,lru减节点。

MySQL系列3:缓冲池Buffer Pool的设计思想的更多相关文章

  1. 【大白话系统】MySQL 学习总结 之 缓冲池(Buffer Pool) 的设计原理和管理机制

    一.缓冲池(Buffer Pool)的地位 在<MySQL 学习总结 之 InnoDB 存储引擎的架构设计>中,我们就讲到,缓冲池是 InnoDB 存储引擎中最重要的组件.因为为了提高 M ...

  2. 【大白话系统】MySQL 学习总结 之 缓冲池(Buffer Pool) 如何支撑高并发和动态调整

    如果大家对我的 [大白话系列]MySQL 学习总结系列 感兴趣的话,可以点击关注一波. 一.上节回顾 在上节< 缓冲池(Buffer Pool) 的设计原理和管理机制>中,介绍了缓冲池整体 ...

  3. MySql 缓冲池(buffer pool) 和 写缓存(change buffer) 转

    应用系统分层架构,为了加速数据访问,会把最常访问的数据,放在缓存(cache)里,避免每次都去访问数据库. 操作系统,会有缓冲池(buffer pool)机制,避免每次访问磁盘,以加速数据的访问. M ...

  4. MySQL · 性能优化· InnoDB buffer pool flush策略漫谈

    MySQL · 性能优化· InnoDB buffer pool flush策略漫谈 背景 我们知道InnoDB使用buffer pool来缓存从磁盘读取到内存的数据页.buffer pool通常由数 ...

  5. (1.16)mysql server优化之buffer pool

    (1.16)mysql server优化之buffer pool 1.innodb buffer pool 查看 show status like  'Innodb_buffer_pool_%'; 该 ...

  6. MySQL · 引擎特性 · InnoDB Buffer Pool

    前言 用户对数据库的最基本要求就是能高效的读取和存储数据,但是读写数据都涉及到与低速的设备交互,为了弥补两者之间的速度差异,所有数据库都有缓存池,用来管理相应的数据页,提高数据库的效率,当然也因为引入 ...

  7. InnoDB 中的缓冲池(Buffer Pool)

    本文主要说明 InnoDB Buffer Pool 的内部执行原理,其生效的前提是使用到了索引,如果没有用到索引会进行全表扫描. 结构 在 InnoDB 存储引擎层维护着一个缓冲池,通过其可以避免对磁 ...

  8. 【转载】MySQL · 性能优化· InnoDB buffer pool flush策略漫谈

    背景 我们知道InnoDB使用buffer pool来缓存从磁盘读取到内存的数据页.buffer pool通常由数个内存块加上一组控制结构体对象组成.内存块的个数取决于buffer pool inst ...

  9. mysql内核源代码深度解析 缓冲池 buffer pool 整体概述

    http://blog.csdn.net/cjcl99/article/details/51063078

  10. MySQL中读页缓冲区buffer pool

    Buffer pool 我们都知道我们读取页面是需要将其从磁盘中读到内存中,然后等待CPU对数据进行处理.我们直到从磁盘中读取数据到内存的过程是十分慢的,所以我们读取的页面需要将其缓存起来,所以MyS ...

随机推荐

  1. 让ChatGPT来写今年的高考作文,能得几分?

    使用最新的ChatGPT4模型,做2023年全国甲卷的高考作文. 作文考试题目如下 人们因技术发展得以更好地掌控时间,但也有人因此成了时间的仆人.这句话引发了你怎样的联想与思考?请写一篇文章. 要求: ...

  2. sql server注入rce实践

    背景:在漏洞挖掘中,合理的利用sql注入,可以把注入转换成rce,使一个高危漏洞变成严重漏洞.在红蓝对抗中,利用注入rce,实现内网横向移动.笔者基于漏洞挖掘和红蓝对抗上遇到的sql server注入 ...

  3. PostgreSQL 12 文档: 部分 VII. 内部

    部分 VII. 内部 这一部分包含PostgreSQL开发者可能用到的各类信息. 目录 50. PostgreSQL内部概述 50.1. 一个查询的路径 50.2. 连接如何建立 50.3. 分析器阶 ...

  4. 前端学习 C 语言 —— GDB调试器

    GDB调试器 我们在讲指针时用 GDB 调试段错误. 本篇将详细介绍 gdb 的最常用命令.日志记录.检测点,最后介绍如何用 gdb 调试进程以及用gdb 调试一个开源项目的调试版本 -- glmar ...

  5. Blazor前后端框架Known功能介绍:系统安装激活及自定义

    本章介绍系统安装与激活及其自定义功能. 概述 框架内置简单的系统安装功能. 录入企业编码.名称.系统名称.产品密钥.管理员密码信息完成安装. 可自定义高级安装功能,如安装数据库等您产品所需的安装信息. ...

  6. Tomcat改jar

    Tomcat改jar 插件修改 <!-- <plugin>--> <!-- <groupId>org.apache.maven.plugins</gro ...

  7. pe文件对齐

    PE中规定了三类对齐:数据在内存中的对齐. 数据在文件中的对齐.资源文件资源数据的对齐. 1.内存对齐 由于windows操作系统对内存属性的设置以也为单位,所以通常情况下,节在内存中的对齐单位必须至 ...

  8. Verilog实现奇分频电路

    在FPGA中,计数器电路用途很广,一般计数器电路都可作为分频电路.实现占空比为50的偶分频电路很好实现.但实现占空比为50的奇分频电路有点难度.下面给出一个简单例子,记录学习奇分频电路的过程. 实现占 ...

  9. 2021-11-17 WPF初识

    StackPanel容器:默认竖直排列,Orientation="Horizontal"横向排列,超过就不会显示 wrapPanel:超过会自动换行 设置样式: <Windo ...

  10. 2021-3-9 保存csv格式文件

    public void SaveCSV(DataTable dt, string fullPath) { FileInfo fi = new FileInfo(fullPath); if (!fi.D ...