参考书籍《mysql是怎样运行的》

系列文章目录和关于我

一丶为什么需要Buffer Pool

对于InnoDB存储引擎的表来说,无论是用于存储用户数据的索引,还是各种系统数据,都是以页的形式存放再表空间中,归根结底还是存储再磁盘上。因此InnoDB存储引擎处理客户端的请求是,如果需要访问某个页的数据,需要把完整的页数据加载到内存中,即便是只需要一条数据,也需要把整个页的数据加载到内存后进行读写访问。如果没用缓存那么一条sql需要进行多次的磁盘IO操作,如果在读写页后将其缓存在内存中,便可以减少这种磁盘IO提高mysql性能。

二丶InnoDB Buffer Pool及其内部组成

InnoDB 会在mysql服务器起到是就向操作系统申请一块连续的内存,(innodb_buffer_pool_size可以控制大小,单位字节)用来对InnoDB的页做缓存操作。

Buffer Pool对应一片连续的内存被划分为若干个页面,页面的大小和InnoDB页面大小一致(16kb)每一个buffer pool 页都对应一些控制信息(表空间编号,页号等)这些控制信息被抽象为控制块(后文我们把buffer pool的页称为缓冲页,和表空间中页做区分)

三丶空闲缓冲页管理——free 链表

从磁盘上读取一个页到buffer pool中时,应该把这个页缓存到哪儿昵。buffer pool的做法时将空闲的缓冲页对应的控制块作为一个节点放在链表中,这个链表称作free链表。

其中有一个基节点负责记录链表的头和尾,每一个空闲的页都将在free 链表中串联起来,每当innodb需要缓存一个页的时候,就通过基节点获取一个空闲的buffer pool 缓冲页,然后在这个缓冲页中记录下表空间,页号之类的信息。然后把缓冲页对应的free链表节点移除。

在缓存一个页的时候,还需要判断当前页是否已经被缓存,innodb 对已经缓存的页,根据其表空间和页号两个值作为hash的key,建立hash表,这样可以很快的进行判断。

四丶缓冲页刷盘——flush链表

当innodb修改一个磁盘上的页并缓存到buffer pool中,这时候内存中缓存的数据和磁盘就不一致这种页称为脏页。如果每次执行完修改都立马将数据刷新到磁盘中的页会影响到程序的性能,所以innodb不会立马刷新到磁盘,而是使用flush链表将脏页对应的控制块串联起来

五丶缓冲空间不够怎么办——LRU链表管理

1.简单的LRU链表

buffer pool的大小毕竟是有限的,当free 链表中不存在更多空闲的缓冲页了,这时候就需要采取一些淘汰策略对一些无用的缓冲页进行淘汰。

这里就是涉及到两个问题:什么样的缓冲页是无用的,如何维护这些缓冲页来实现此淘汰策略。这时候自然是使用LRU算法(最近最少使用)淘汰最近最少使用到缓冲页。LRU算法使用一个链表来实现,当innodb访问某个页的时候:

  • 如果该页不在buffer pool中,那么把该页从磁盘加载到buffer pool中的缓冲页是,就把该页的控制块放在LRU链表的头部
  • 如果该页已经在buffer pool中,那么移动节点到LRU链表头部

这样可以实现,被使用到缓冲页,会尽量靠近LRU链表的头部,自然而然尾部便是最近最少使用到的数据。LRU算法基于——最近使用到的数据,后续也会到使用到的思想,使用LRU可以提高Buffer pool缓存的命中率。

2.简单LRU链表无法解决的问题

  • 预读

    innodb认为在执行当前请求的时候,后续可能会读取某些页面的时候,会把这些页面也加载到buffer pool

    1. 线性预读

      如果顺序访问某个区的页面超过innodb_read_ahead_threshold的值,那么会触发一次线性预读,异步的读取下一个区中全部的页面到buffer pool中。

    2. 随机预读

      如果某个区的13个连续的也都被加载到buffer pool中,无论是否是顺序读取的页面,都会异步读取本区中所有的其他页面到buffer pool中,innodb_random_read_ahead 设置为on可以开启随机预读

    预读的目的是提高语句的执行效率,相当于innodb 认为你会用到,异步的帮你加载到缓存中,后续不需要继续读磁盘。但是在LRU的管理中,如果预读的页面很多没用用到的话,还将预读的页面放在链表头部,后续淘汰的页面反而是需要用到的,会极大的降低缓存命中率。预读导致加载到buffer pool中页的不一定会使用到

  • 全表扫描语句

    当一个sql没有合适的索引或者没用where限定条件的时候,innodb会扫描该表聚集索引所有的页。如果页非常多,buffer pool无法容纳的时候,就会把其他有用的缓冲页进行淘汰,降低缓存命中率。全表扫描导致许多使用频率低的页被同时加载到buffer pool中,导致使用频率高的页从buffer pool中被移除(这里可以看出LFU算法的好处,哈哈哈)

3.innodb 如何解决预读和全表扫描导致缓存命中率降低的问题

innodb 根据一定比例将LRU链表分为两部分:

  • 热数据区:使用频率很高的缓冲页构成,称为young区
  • 冷数据区:使用频率不是很高的缓存页构成,称为old区

innodb_old_bolocks_pet可以设置old区占用的比列,默认是37%

3.1解决预读页面后续也许使用不到的问题

innodb规定当磁盘某个页面在初次加载到buffer pool中某个缓冲页时,该缓冲页对应的控制块会放在old区域的头部,这样预读到的且后续如果不进行后续访问的页面会逐渐从old区移除,而不影响young区使用频率高的缓冲页。

3.2解决全表扫描短时间访问大量使用频率低页面的问题

在进行全表扫描时,虽然首次访问放在old区头部,但是后续会马上被访问到,这时候会把该页放在young区域的头部,这样依旧会影响到使用频率高的页面。

为了解决这个问题,innodb规定对于某个处于old区的缓冲页第一次访问时,就在其控制块中记录下访问时间,如果后续访问的时间和第一次访问的时间,在某个时间访问间隔内(innodb_old_blocks_time可以进行设置)那么页面不会从old区移动到young区,反之移动到young区中。这个时间间隔默认时1000ms,基本上多次访问同一个页面中的多个记录的时间不会超过1s。

3.3 优化每次都需要移动young区节点到LRU链表头部的问题

如果每次访问一个缓冲页都需要移动到LRU链表的头部,像young区中这种热点数据,每次都需要更新链表头部,并且这还是一个高并发操作,需要CAS或者锁,开销也不小。为了解决这个问题 innodb规定只有被访问的缓冲页位于young区的前1/4范围外,才会进行移动,所以前1/4的高热度的数据,不会频繁移动

六丶脏页刷盘

innodb后台有专门的线程负责将脏页刷新到磁盘

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

    后台线程定时从LRU链表尾部扫描一些页面,扫描的页面数量可以通过innodb_lru_scan_depth指定,如果在LRU中发现脏页,那么刷新到磁盘

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

    后台线程也会定时从flush链表中刷新一部分页面到磁盘,刷新速率取决于系统是否繁忙

如果后台线程刷新的很慢,且有新的页面需要进行缓存,这时候会从LRU链表尾部看看是否有可以直接释放的非脏页,如果不存在那么需要刷盘然后缓存新的页。

这里我们可以看到buffer pool没用保证修改的数据一定被磁盘持有化,那么事务的持久性如何实现昵,怎么保证mysql服务突然挂了,已经提交的事务不会丢失昵,这就得提到redo log了

七丶多个buffer pool实例

在并发量比较大的时候,多个线程操作同一个buffer pool,必然涉及到同步机制,影响到请求的处理速度,所以在buffer pool比较大的时候,会被拆分成多个小的buffer pool,独立进行使用,在高并发的时候不会相互影响(虽然也不能公用彼此的缓存内容)提高并发处理能力。只有在innodb_buffer_pool_size设置的buffer pool大小大于1g的时候,通过innodb_buffer_pool_instances设置的buffer pool实例个数才会生效

八丶动态的扩大缩小buffer pool

为了能够在运行的时候动态的扩大缩小buffer pool,innodb提出chunk的概念,innodb 不在一次申请为某一个buffer pool申请一大片连续的内存空间,而是以chunk作为单位进行申请。一个chunk就是一个连续的内存空间,其内部包含了若干缓冲页和其对应的控制块。

可以通过innodb_buffer_pool_chunk_size设置每一个chunk的大小,默认时128mb。

所以我们最好让innodb_buffer_pool_size = innodb_buffer_pool_chunk_size x innodb_buffer_pool_instances的若干倍保证每一个buffer pool实例中chunk数相同,如果innodb_buffer_pool_chunk_size x innodb_buffer_pool_instances大于innodb_buffer_pool_size innodb_buffer_pool_chunk_size 会自动被调整为innodb_buffer_pool_size / innodb_buffer_pool_instances的大小

Mysql InnoDB Buffer Pool的更多相关文章

  1. [转]MySQL innodb buffer pool

    最近在对公司的 MySQL 服务器做性能优化, 一直对 innodb 的内存使用方式不是很清楚, 乘这机会做点总结. 在配置 MySQL 的时候, 一般都会需要设置 innodb_buffer_poo ...

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

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

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

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

  4. 理解innodb buffer pool

    今天组里有个同事说可以查看innodb buffer pool每个表和索引占的大小,为此我搜了下,还真有方法,记录下. innodb buffer pool有几个目的: 缓存数据--众所周知,这个占了 ...

  5. innodb buffer pool小解

    INNODB维护了一个缓存数据和索引信息到内存的存储区叫做buffer pool,他会将最近访问的数据缓存到缓冲区.通过配置各个buffer pool的参数,我们可以显著提高MySQL的性能. INN ...

  6. innodb buffer pool相关特性

    背景 innodb buffer pool作为innodb最重要的缓存,其缓存命中率的高低会直接影响数据库的性能.因此在数据库发生变更,比如重启.主备切换实例迁移等等,innodb buffer po ...

  7. Innodb buffer pool/redo log_buffer 相关

    InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理.在数据库系统中,由于CPU速度和磁盘速度之前的鸿沟,通常使用缓冲池技术来提高数据库的整体性能. 1. Innodb_buffe ...

  8. 14.6.3.4 Configuring InnoDB Buffer Pool Prefetching (Read-Ahead) 配置InnoDB Buffer pool 预取

    14.6.3.4 Configuring InnoDB Buffer Pool Prefetching (Read-Ahead) 配置InnoDB Buffer pool 预取 一个预读请求是一个I/ ...

  9. 14.6.3.1 The InnoDB Buffer Pool

    14.6.3.1 The InnoDB Buffer Pool InnoDB 保持一个存储区域被称为buffer pool 用于cache数据和索引在内存里, 知道InnoDB buffer pool ...

随机推荐

  1. DS二叉树——二叉树之数组存储

    题目描述 二叉树可以采用数组的方法进行存储,把数组中的数据依次自上而下,自左至右存储到二叉树结点中,一般二叉树与完全二叉树对比,比完全二叉树缺少的结点就在数组中用0来表示.,如下图所示 从上图可以看出 ...

  2. Oracle-查询之函数

    DQL语句:查询语句用于查询表中数据DQL必须包含的两个部分:select 子句 和from 子句select子句:指定要查询的内容,可以指定表中的具体字段,*号,函数,或者表达式from子句:指定数 ...

  3. 我就获取个时间,机器就down了

    本文主要讲解linux 时间管理系统中的一个问题 背景:linux 时间管理,包含clocksource,clockevent,timer,tick,timekeeper等等概念 , 这些概念有机地组 ...

  4. 一些有用的数学知识(Updating)

    文章目录 拉格朗日插值公式 微分中值定理 费马引理 拉格朗日中值定理 柯西中值定理 洛必达法则 连分数(NOI2021 D2T2 考点) 定义 结论 定理1 定理2 定理3 定理4 定理5 欧拉公式 ...

  5. MyBatis快速上手与知识点总结

    目录 1.MyBatis概述 1.1 MyBatis概述 1.2 JDBC缺点 1.3 MyBatis优化 2.MyBatis快速入门 3.Mapper代理开发 3.1 Mapper代理开发概述 3. ...

  6. vue2和vue3的区别?

    vue2和vue3的主要区别在于以下几点: 1.生命周期函数钩子不同 2.数据双向绑定原理不同 3.定义变量和方法不同 4.指令和插槽的使用不同 5.API类型不同 6.是否支持碎片 7.父子组件之间 ...

  7. KingbaseES V8R6集群维护案例之---停用集群node_export进程

    案例说明: 在KingbaseES V8R6集群启动时,会启动node_exporter进程,此进程主要用于向kmonitor监控服务输出节点状态信息.在系统安全漏洞扫描中,提示出现以下安全漏洞: 对 ...

  8. 解决国内 github.com 打不开的准确方法

    前言 github是目前比较公认的一个开源网站,对于像我们这类使用机器学习进行科学计算的研究人员来讲,github提供了代码开源,验证原文献中计算结果正确性的一个平台. 到目前为止,几乎所有使用机器学 ...

  9. c#中容易被忽视的foreach

    有句俗语:百姓日用而不知.我们c#程序员很喜欢,也非常习惯地用foreach.今天呢,我就带大家一起探索foreach,走,开始我们的旅程. 一.for语句用的好好的,为什么要提供一个foreach? ...

  10. 三门问题模拟对比试验——附VB6和Python3源程序及EXE文件

    三门问题模拟对比试验 [直接跳转到文件下载] 前言 "三门问题"是一个很有意思的概率论问题,涉及贝叶斯公式,是人工智能领域的一个经典问题. 为了直观地研究这个问题,也为了测试一下P ...