Code Ease Code Ease
  • 个人博客网站 (opens new window)
  • 好用的工具网站 (opens new window)
  • Java核心基础
  • 框架的艺术
  • 分布式与微服务
  • 开发经验大全
  • 设计模式
  • 版本新特性
数据库系列
大数据+AI
  • xxl-job
运维与Linux
  • 基于SpringBoot和BootStrap的论坛网址
  • 基于VuePress的个人博客网站
  • 基于SpringBoot开发的小功能
  • 做一个自己的IDEA插件
程序人生
关于我
  • 分类
  • 标签
  • 归档

神秘的鱼仔

你会累是因为你在走上坡路
  • 个人博客网站 (opens new window)
  • 好用的工具网站 (opens new window)
  • Java核心基础
  • 框架的艺术
  • 分布式与微服务
  • 开发经验大全
  • 设计模式
  • 版本新特性
数据库系列
大数据+AI
  • xxl-job
运维与Linux
  • 基于SpringBoot和BootStrap的论坛网址
  • 基于VuePress的个人博客网站
  • 基于SpringBoot开发的小功能
  • 做一个自己的IDEA插件
程序人生
关于我
  • 分类
  • 标签
  • 归档
服务器
  • MySQL

    • 一文汇总数据库基础知识点!
    • 简单聊聊MySQL中的六种日志
    • 图解Mysql索引的数据结构!看不懂你来找我
    • 白话Mysql的锁和事务隔离级别!死锁、间隙锁你都知道吗?
    • 数据库分库分表扫盲,不会用也得知道概念
    • Mysql的索引调优详解
    • InnoDB的缓存池
      • InnoDB中的页
      • Buffer Pool 存了哪些东西
      • BufferPool如何管理Page页
        • Free List
        • LRU List
        • Flush List
      • 脏页如何被刷新到磁盘
      • Buffer Pool 的运行状态
    • InnoDB的插入缓冲和自适应哈希索引
    • MySQL监控工具
    • 删库了别跑路 你还应该学会如何恢复数据
  • Redis

  • MongoDB

  • 数据库系列
  • MySQL
CodeEase
2023-11-29
目录

InnoDB的缓存池

作者:鱼仔
博客首页: codeease.top (opens new window)
公众号:Java鱼仔

# InnoDB中的页

InnoDB存储引擎基于页的方式存储在磁盘中,它将数据存储在页(Page)的概念上。一个页是 InnoDB 存储引擎中的最小存储单位,它包含了一定数量的数据行、索引节点和其他元数据。

InnoDB 存储引擎中的页包括了数据页和索引页两种类型:

数据页:数据页是存储数据行的页,每个数据页的大小可以通过参数 innodb_page_size 进行配置,默认为 16KB。在数据页中,InnoDB 存储了多个数据行,每个数据行包含了一条记录的所有列数据。

索引页:索引页是存储索引节点的页,每个索引页的大小也可以通过参数 innodb_page_size 进行配置,默认为 16KB。在索引页中,InnoDB 存储了多个索引节点,每个索引节点包含了一个索引键值和一个指向数据页的指针。

但是如果每次查询数据都从磁盘中去查询,就会导致性能很差,于是InnoDB增加了一个缓冲池(Buffer Pool)的东西,用来提高数据库的读写性能。而缓冲池中保存的就是以链表形成的页。

# Buffer Pool 存了哪些东西

Buffer Pool 的大小可以通过参数 innodb_buffer_pool_size 来查看和设置,以我安装的 MySQL 8.0 为例,innodb_buffer_pool_size 默认大小为128M

show variables like 'innodb_buffer_pool_size';
1

一般生产环境中,如果机器的内存大小是 32G,可以将 innodb_buffer_pool_size 大小设置为16G。

缓冲池并非只有一个,通过 innodb_buffer_pool_instances 参数可以查看和设置缓冲池的个数,默认是一个缓冲池。

show VARIABLES like 'innodb_buffer_pool_instances'
1

缓冲池中除了缓冲了数据页和索引页,还缓冲了插入缓冲、自适应哈希索引、锁信息、数据字典信息等等,只不过主要还是缓冲数据页和索引页。

7-1.jpeg

Buffer Pool使用控制块控制着各种页,,控制块中存储着对应缓存页,控制块中存储着对应缓存页的所属的表空间、数据页的编号,以及对应缓存页在Buffer Pool中的地址等信息。而控制块的大小约为页的5%,大概是800字节。

# BufferPool如何管理Page页

BufferPool将所有的Page页分为了三种类型:

  • free page:空闲page,未被使用
  • clean page:被使用page,数据没有被修改过
  • dirty page:脏页,page页中数据和磁盘数据发生了不一致

接着Buffer Pool中使用了三种链表来管理这些页:

  • free list管理free page,free list把所有空闲的缓冲页对应的控制块作为一个个的节点放到一个链表中。
  • flush list管理dirty page,内部page按修改时间排序。Innodb为了提高处理效率,在每次修改缓冲页后,并不是立刻把修改刷新到磁盘上,而是在未来的某个时间点进行刷新操作,所以需要使用flush list 存储dirty page,凡是被修改过的缓冲页对应的控制块都会作为节点加入到flush链表。
  • lru list:表示正在使用的缓冲区,管理clean page和dirty page,缓冲区以midpoint为基点,前面链表称为new 列表区,存放经常访问的数据,占63%,后面链表称为old列表区,存放使用较少的数据,占37%。

# Free List

当数据库刚启动时,所有的页都在Free List中,当有页需要存入到Buffer Pool的时候,首先判断Free List中是否有空闲的页,如果有的话就将该页从Free List中剔除,然后放入到LRU List中。

# LRU List

LRU List是Buffer Pool最核心的部分,InnoDB取缓存的数据取得就是 LRU List 的数据页。为了确保尽可能让热点数据在缓存中,LRU List使用了改进版本的LRU算法来实现。

传统的LRU算法会将新加载的数据存放到LRU列表的表头,最近一次访问过的数据也会放到表头,优先淘汰表尾的数据,这样能保证最近经常使用的数据不会被淘汰。

但是在MySQL中如果发生全表扫描或者一些预读机制,很多只会用到一次的数据就进入到了LRU的表头,导致真正的热数据被淘汰,因此MySQL改造了LRU算法。

7-2.jpeg

将LRU拆分为热数据区和冷数据区,每次数据都插入到midpoint开始插入,如果该数据页在LRU链表中超过1s,就进入到热数据区,如果短于1s,则位置不变,1s这个参数由innodb_old_blocks_time控制, 默认为1s。

show VARIABLES like 'innodb_old_blocks_time'
1

其中热数据区和冷数据区的大小通过 innodb_old_blocks_pct 进行控制,innodb_old_blocks_pct 代表着冷数据区的大小,默认为37%

show VARIABLES like 'innodb_old_blocks_pct'
1

# Flush List

Flush List 管理着 Buffer Pool 中的 dirty page,Innodb为了提高处理效率,在每次修改缓冲页的数据之后,并不是立刻把修改刷新到磁盘上,而是在未来的某个时间点进行刷新操作,所以需要使用flush list 存储dirty page,凡是被修改过的缓冲页对应的控制块都会作为节点加入到flush链表。 InnoDB 使用 redo log 来避免数据库崩溃但脏页数据还没写入的问题。当执行更新操作时,InnoDB会先把数据写入到 redo log,再写入数据库,保证数据不丢失。

# 脏页如何被刷新到磁盘

缓冲池的大小毕竟是有限的,虽然数据被修改后不会立刻被刷新到磁盘,但是依然需要在满足一定条件的情况下将脏页从缓冲池刷新到磁盘中。在以下几种场景下,脏页会被刷新到磁盘中:

  • 数据库正常关闭:当数据库被正常关闭之前,脏页会被刷新到磁盘中。
  • redo log 日志满了:MySQL在执行数据变更前会先将变更写入 redo log,保证了数据的持久性,因此在redo log将要满的时候也会把脏页刷新到磁盘中。
  • 后台线程刷新:InnoDB有异步的刷新线程和合并线程,这些线程会定期或定时地将脏页刷新到磁盘中。具体来说,InnoDB的刷新线程会根据redo日志和LSN(日志序列号)信息,将脏页刷新到磁盘中;InnoDB的合并线程则会将多个小的脏页合并成一个大的脏页,以减少磁盘IO操作的次数。
  • LRU列表淘汰:当LRU列表满了,那些被淘汰的页如果包含脏页,这些脏页就会被刷新到磁盘。
  • 主动刷新:可以通过 FLUSH TABLES 命令主动将表的缓存刷入到磁盘中。一般情况下不会主动去刷新缓存。

# Buffer Pool 的运行状态

通过下面这条命令可以看到InnoDB的整体运行状态

show engine innodb status;
1

通过执行上面的命令可以得到一段很长的日志信息

Per second averages calculated from the last 16 seconds
1

开头这一行表示这是过去16秒的数据库信息

其中 BUFFER POOL AND MEMORY 下面的表示 BUFFER POOL 的运行状态

----------------------
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 0
Dictionary memory allocated 448265
Buffer pool size   8192
Free buffers       7137
Database pages     1046
Old database pages 366
Modified db pages  0
Pending reads      0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 165, not young 3631
0.00 youngs/s, 0.00 non-youngs/s
Pages read 887, created 160, written 327
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
Buffer pool hit rate 1000 / 1000, young-making rate 0 / 1000 not 0 / 1000
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 1046, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

有几个关键性的数据:

Buffer pool size :表示缓冲池有 8192 个页,占用大小为 8129*16K

Free buffers: Free 列表页的数量

Database pages:LRU 列表页的数量

Modified db pages:Flush list 页大小

其中 Pages made young 165, not young 3631 表示数据从old区域移动到young区域的次数,made young表示移动次数,not young未移动次数。

Buffer pool hit rate:缓冲池的命中率,这个比率100%说明缓冲池运行良好。若该值小于95%说明要看一下是否是全表扫描等操作污染了LRU列表。

上次更新: 2025/04/29, 17:22:06
Mysql的索引调优详解
InnoDB的插入缓冲和自适应哈希索引

← Mysql的索引调优详解 InnoDB的插入缓冲和自适应哈希索引→

最近更新
01
AI大模型部署指南
02-18
02
半个月了,DeepSeek为什么还是服务不可用
02-13
03
Python3.9及3.10安装文档
01-23
更多文章>
Theme by Vdoing | Copyright © 2023-2025 备案图标 浙公网安备33021202002405 | 浙ICP备2023040452号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式