• 细说InnoDB缓冲池 buffer pool(free、flush、lru)


    细说InnoDB缓冲池 buffer pool(free、flush、lru)
     2021-12-12 21:57:42     小道仙     126阅读     2评论

    视频地址 https://www.bilibili.com/video/BV1C3411t7WL

    Table of Contents

    一、开篇

    在InnoDB引擎中对数据库增删改查,都是先从磁盘中把数据加载到内存,然后在内存中进行相关操作,我们把这块的内存称之为 buffer pool (缓冲池)

    既然这是内存中的一块区域,那么它就一定有大小(默认是128M),如果你有一个16G的数据库服务器,你安装好MySQL,那你的缓冲池的大小就是128M,这时候肯定很影响你的操作,因为它太小了,所以我们应该根据当前条件来对这个参数进行修改:innodb_buffer_pool_size

    说到数据库,大家脑海里肯定是库、表、行、字段,这其实没错毕竟我们对它就是这样操作的,这其实也错,因为我们知道数据是存在硬盘中的,硬盘怎么会有这些个逻辑数据呢?肯定是都存储的物理数据。

    在硬盘中,MySQL把16KB作为一个单位,我们把这个叫做数据页,一页里面可以存储很多的数据,页之间是有双向指针进行关联的。

    从硬盘中读取数据到缓冲池也是以页为单位的,在缓冲池中有一个叫做缓存页的东西,它也是16KB大小,每读取一个数据页就把它放在一个空闲的缓存页里面。

    除了这个缓存页之外还有一个叫做元数据的东西,你可以这样理解这个每个元数据和每个缓存页一一对应,它相当于是缓存页的头,它里面存储了缓存页的基础信息,下面你就会明白它的作用了。

    好了,到这里我们就知道了buffer pool里面有缓存页(16KB),元数据,那么你的脑海里应该浮现这样的图

    image.png

    buffer pool默认的大小128M,是用来存储数据的,也就是缓存页,但同时也还要存储元数据,所以实际上它的大小会比128M要大一些。


    二、free链表

    上面说到,我们是把硬盘中的数据页一片片加载到buffer pool的缓存页里面去,那这里就存在一个问题:怎么知道哪些数据页是空的呢?我们不可能去覆盖别的数据对不对?

    其实这里维护了一个free链表,它是由空闲的元数据组成的(前面我们说了这个元数据和缓存页是一一对应的,所以一个空闲元数据就对应一个空缓存页),这样我们就很容易的知道那些缓存页是空的、还有多少空的缓存页。

    image.png

    这个基本结点是单独的空间,它里面就存储了链表长度等其它的数据。

    如果有新的数据页要加载到缓存中,就从free链表里面取出一个元数据找到对应的缓存页,然后放进去,在把这个元数据从free链表里面剔除。

    可以理解成你在Java中的链表,伪代码实现 head.next = head.next.next


    三、flush 链表

    上面的free链表是标示空闲的缓存页,而我们的修改数据也是在缓存页里面操作的,这些数据并未存储到磁盘中去,毫无疑问肯定是要记录哪些缓存页是已经修改了的,不然系统怎么把它们刷进磁盘呢?而这个记录的地方也就是 flush链表

    flush链表也是和上面free链表一样:基于元数据组成的。当有一个缓存页被修改之后就把它加入到flush链表里面去。

    注:可能有人有疑问为啥这个元数据可以组成free链表又能组成flush链表呢?其实熟悉链表结构的朋友都知道,链表只是一个指向的问题而已,我们可以在元数据里面定义N个,next、front,指针去指向就好了。

    因为flush链表和free链表结构基本上是一样的,所以就不花了,但是要理解不是单独的两个链表,而是两种指向。

    有了flush链表,当要把数据刷回磁盘的时候就容易多了,具体何时刷入下面再说。


    四、LRU 链表

    这里我们还有一个问题,我们的数据都是存储在缓存中的,而缓存大小是有限的,有些数据很可能就是查询一次就不再使用了,如果让它一直存储在缓存中那肯定是不合适的。所以这里我们引入一个新的链表 LRU链表 (Least Recently Used)最近最少使用的意思,也就是我们可以使用这个链表来维护热点和非热点数据。

    注:这个链表的节点也是基于元数据来组成的,但是和上述两个链表还是有些差异。

    何为热点数据,这其实是一个定义问题。当然按照顺序排序把最近访问的数据放到链表头部,那这样的话前面的数据就是热点数据。

    但是MySQL有两个操作会让上面这种方式产生漏洞,全表扫描预读


    全表扫描

    这很好理解,比如你写一个不带任何条件的查询SQL: SELECT * FTOM t_user,这会加载很多的数据页到缓存中,但实际上很多数据只用一次就不再使用了。


    预读

    在InnoDB引擎下,当你一次读取超过56页数据的时候就会触发预读,也就是在InnoDB看来,如果你一下次读取了56页数据,那么你大概率也是要读取57、58…

    这个56是来自默认的配置,当然你页可以修改它的配置文件 innodb_read_ahead_threshold


    冷热区域

    为了解决上面的这个“漏洞”,真实的LRU链表是分冷热区域的,当数据页从磁盘加载到内存的时候,会把它放在冷区域的头部,当超过1s后再次去访问这个数据的时候才会把它移动到热区域。

    • innodb_old_blocks_pct 冷区域的大小默认是 37%
    • innodb_old_blocks_time 多少毫秒之后访问冷数据,会加载到热区域 默认 1000

    按照我们的理解如果超过1s后再访问冷区域的数据就把它移动到热区域,这个是没问题的。

    但如果我们访问热区域的数据,是不是每次访问就把它移动到热区域的头部呢?这样看似合理,实则不合理,你想想频繁的移动也是需要消耗性能的,所以只有在访问热区域的后75%的数据才会移动到热区域的头部。

    image.png


    五、数据回盘

    buffer pool是内存的一块区域,它总会有满的时候,如果它满了那怎么办呢?其实通过上面的几个链表我们已经知道了,如果满了,可以把LRU链表的冷区域的数据进行刷回磁盘操作就好了。

    另外一个问题就是flush链表里面的脏页何时被刷回磁盘呢?它有两个操作,一个是上面的磁盘空间不足的时候会把LRU链表里面的冷数据刷回磁盘,这里面也包含了一些flush链表里面的数据,一个是后台有一个IO线程会不定期的把flush链表里面的脏页刷回磁盘。

    注:free、flush、lru 都是指的相同的数据,只不过是按照不同的指向组成了不同的链表而已。

     
    转载:https://www.xdx97.com/article/919709627669544960
  • 相关阅读:
    快排 [模板]
    翻硬币
    Euphoria与量子波动速读
    高精度例题
    Div3 595 E
    Div 595 C1 C2
    常用 STL 整理
    CF 595 Div3 B2
    【思维】复杂度均摊+并查集——icpc cerc 2019 Saba1000kg
    离散化+圆直线交点+转化——icpc cerc 2019 D
  • 原文地址:https://www.cnblogs.com/wugh8726254/p/16822756.html
Copyright © 2020-2023  润新知