缓存池
概述
作用
- 加速读: 数据读取先查缓存,没有再查磁盘
- 加速写:数据修改后,先写入缓存, 再找时间再统一写入磁盘
原理
局部性原理
- 空间局部性:某页被访问,其相邻页也可能会被访问 -> 预读机制
- 时间局部性:某页最近被访问过,将来也很可能会被访问 -> LRU
- 目的是减少磁盘 IO 次数,尽可能的让读写落到内存中
- 按数据页读取,存到内存中
有关预读
https://www.cnblogs.com/geaozhang/p/7397699.html
线性预读
线性预读是以 extend 为单位的。通过判断当前的 extend 中的数据是否是连续访问的,并以此来预测是否有必要把下一个 extend 提前从磁盘文件中读取出来,加载到 buffer pool 中。
相关配置
- innodb_read_ahead_threshold: 当一个 extend 中,大于等于 innodb_read_ahead_threshold 个 page 是被连续访问的时候,就预先加载下一个 extend 的数据。默认值是 56,最大值 64
数据结构
缓存池结构
缓存池实例结构
缓存池实现上有三种链表结构
- Free 链表:管理空白页
- LRU 链表:管理已经有数据的页
- Flush 链表:管理脏页,脏页同时也会在 LRU 链表中
- 读取数据未命中缓存,会向 Free 链表要一页,加入到 LRU 链表中
- 内存中的页被修改,会扔到 Flush 链表一份
- Flush 链表的页被回存了,会在 Flush 链表中删除
淘汰策略
缓存大小有限,需要一定的淘汰策略
基础淘汰策略:LRU(最近最少使用)
冷热数据分离
原有纯 LRU 方案可能会存在以下问题:
- 预读失效:预读会加载相邻页,有可能相邻页并没有再被访问,却挤掉了被频繁访问的页
- 缓冲池污染: 存在扫描的 sql 请求,此请求得到的数据不会被频繁使用,占用页巨多,却将其他的缓存页给挤掉了
因此引入了 冷热数据分离的设计:
- 当数据页第一次被加载到缓冲池中的时候,先将其放到冷数据区域的链表头部,1s(由 innodb_old_blocks_time 参数控制) 后该缓存页被访问了再将其移至热数据区域的链表头部。
- Mysql 中优化为热数据区的后 3/4 部分被访问后才将其移动到链表头部去,对于前 1/4 部分的缓存页被访问了不会进行移动。
配置
- innodb_buffer_pool_chunk_size
- innodb_buffer_pool_instances
- innodb_buffer_pool_size
可在线配置
修改缓存池总大小,单位字节,必须为 innodb_buffer_pool_chunk_size*innodb_buffer_pool_instances 的倍数。
SET GLOBAL innodb_buffer_pool_size=8589934592;
使用 Innodb_buffer_pool_resize_status 报告缓冲池大小调整的进展
mysql> SHOW STATUS WHERE Variable_name ='InnoDB_buffer_pool_resize_status';
+----------------------------------+-------+
| Variable_name | Value |
+----------------------------------+-------+
| Innodb_buffer_pool_resize_status | |
+----------------------------------+-------+
1 row in set (0.01 sec)
只可离线配置
innodb_buffer_pool_chunk_size
[mysqld]
innodb_buffer_pool_chunk_size = 134217728
监控
查看统计信息
show engine innodb status
应用
参考资料
关于 MySQL buffer pool 的预读机制 - GeaoZhang - 博客园
MySQL 中的这个池子--缓冲池(buffer pool),真的强! - 掘金
https://cloud.tencent.com/developer/article/1779059