• 后端进阶知识 MySQL为什么那么快 图文详解 之 flush 链表 与 LRU链表


    可关注专栏 ====》MySQL 进阶知识
    收藏点赞加关注

    脏数据

    基于 free 链表找到一块空闲的缓存页写入数据后,然后更新了这个缓存页,此时缓存页中的数据就与磁盘中的数据页不一致了,那么这个缓存页就是脏数据或者说脏页。最终在内存里更新的这些脏页是会被刷入磁盘的,但是不可能所有的缓存页都刷入磁盘,因为有些缓存页根本没有更新过。所以需要一个数据结构来保存这些被修改过的数据页。
    flush 链表

    与 free 链表类似,flush 链表也是一个双向链表,在元数据中同样保存着 flush_pre 和 flush_next 这样一对指针。元数据节点从 free 链表中取出写入数据到缓存页,如果缓存页被修改了,那么该元数据就被加入到 flush 链表,这些 flush 节点就是被修改过的数据页元数据,等待被刷入磁盘。所以通过 flush 就可以知道哪些缓存页是脏页了。

    在这里插入图片描述

    缓存淘汰

    现在我知道数据页要加载到缓存,需要通过 free 链表找到一个空闲的缓存页,然后把数据写入缓存页。但是缓存页的数量是有限的,当缓存页用尽了该咋办呢?应该通过一定的机制把一些缓存页刷回磁盘,空闲一些缓存页出来。那么哪些缓存页需要被刷入磁盘呢?当然是那些不经常使用的缓存页给刷入磁盘啦,这时就需要引入 LRU 链表。
    LRU 链表

    与 free 链表类似,LRU 链表也是一个双向链表。元数据从 free 链表中取出,然后对应的缓存页被写入数据,同时元数据加入 LRU 链表。而且只要缓存页被访问(包括查询和修改)就会被移动到链表头部,这样当缓存页不足时,把链表尾部的缓存页刷入磁盘就可以空闲出缓存页了。
    在这里插入图片描述

    数据页频繁淘汰

    缓存预读

    当数据页加载到缓存时会连带着把相邻的数据页一同加载到缓存,而不用每次读数据时,都从磁盘加载到缓存,以减少 IO 次数。但是这样一来,很可能预读的缓存根本没有用到,却又占用了缓存页并且处于 LRU 链表头部,而 LRU 尾部的位置可能又使用的频繁。那么恰巧这时缓存页不足需要淘汰时,就会把尾部经常使用的淘汰掉,而预加载的从未使用的却保留了下来。这显然是不合理的。触发预读机制的参数:

    innodb_read_ahead_threshold:默认为 56,如果顺序访问一个数据区的数据页超过这个阈值就会触发预读机制,把下一个数据区的所有数据页都加载到缓存。

    innodb_random_read_ahead:默认为 OFF 关闭的,如果一个数据区连续 13 个数据页被访问,此时会触发预读机制,把这个数据区的所有数据页加载到缓存。
    在这里插入图片描述

    全表扫描

    全表扫描会把表中的数据全部加载到缓存,加入到 LRU 链表的头部,而全表扫描的这些数据可能只会使用一次,但是链表尾部的那些缓存页可能经常使用。淘汰缓存页时就会把那些经常使用的缓存页淘汰掉,而全表扫描的缓存页却保留了下来。
    在这里插入图片描述

    数据冷热分离

    为了解决预加载和全表扫描带来的问题,MySQL 设计了数据冷热分离的 LRU。也就是说实际上 LRU 链表并不是一个单纯的链表,它分为了热数据区和冷数据区,使用 innodb_old_blocks_pct 参数来控制冷数据区占整个链表的比例,默认为 37 。那么数据页首次加载到缓存时,实际上处于冷数据区的缓存页头部。

    MySQL 有个参数 innodb_old_blocks_time 默认值为 1000,在 1000 ms 后再次访问冷数据区头部的缓存页时就会被移动到热数据区的头部了,并不是立即访问就会移动。

    所以这时预加载和全表扫描加载的缓存页会被放在冷数据区,而热数据区的缓存页只要被访问就会一直在热数据区,也就不会导致频繁访问的缓存页被淘汰了。

    基于这样 LUR 冷热分离机制,MySQL 就会优先淘汰冷数据区尾部的缓存页。
    在这里插入图片描述

    再就是 MySQL 对这套机制进行了优化。按照现有规则会把热数据区访问的数据页移动到头部,如果说热数据区的缓存页使用的极为频繁,那么这种移动将是极为损耗性能的,而且也没有必要。所以对这个规则优化后,只有在热数据区的后面 3/4 的缓存页被访问后会移动到热数据区头部,前面的 1/4 部分不会发生移动,这样就尽可能的减少了链表中的节点移动了。

    刷盘

    LRU 尾部的缓存页是如何淘汰刷入磁盘的?首先并不是缓存页耗尽时才会把 LRU 冷数据区尾部的缓存页淘汰刷入磁盘的。MySQL 会有一个定时线程去扫描,每隔一段时间把一些缓存页写入磁盘,留出空闲的缓存页,从 flush 链表移除,加入 free 链表。

    而热数据区也有可能存在被修改的缓存页,这个定时线程会在适当的时机把 flush 中的缓存页都刷入磁盘。被刷入磁盘的缓存页会从 flush 链表和 LRU 链表移除。

    如果实在没有空闲的缓存页,此时又有新的数据页要加载到缓存,那么会从 LRU 链表的冷数据区尾部淘汰刷盘,空闲出缓存页。
      在这里插入图片描述

  • 相关阅读:
    SpringMVC获得请求头和文件上传
    ObjectProvider学习
    14. SpringBoot项目之数据保存到数据库
    天池:数据分析达人赛1:用户情感可视化分析
    [Linux] 网络套接字编程之实现简单的TCP网络程序(下)
    OpenAI 称其在发布检测 ChatGPT 写作的工具时采取“审慎态度”
    SpringSecurity系列 - 09 SpringSecurity 自定义认证数据源实现用户认证
    软件运维面试题
    git:一、GIT介绍+安装+全局配置+基础操作
    死锁,死锁避免
  • 原文地址:https://blog.csdn.net/weixin_44994494/article/details/128017372