Buffer Pool与LRU算法
Buffer Pool是InnoDB最重要的内存结构,缓存数据页和索引页,减少磁盘IO。
Buffer Pool结构
整体架构
SQL
┌─────────────────────────────────────────────────┐
│ Buffer Pool │
├─────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────┐ │
│ │ Data Page Cache │ │
│ │ (数据页/索引页缓存) │ │
│ └─────────────────────────────────────────┘ │
├─────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ Flush List │ LRU List │ │
│ │ (脏页链表) │ (页面淘汰链表) │ │
│ └─────────────┘ └─────────────────────────┘ │
├─────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ Free List │ Page Hash Map │ │
│ │ (空闲页链表) │ (页哈希索引) │ │
│ └─────────────┘ └─────────────────────────┘ │
└─────────────────────────────────────────────────┘
页面结构
SQL
每个数据页默认16KB
┌────────────────────────────────────┐
│ Page Header (页头) │
├────────────────────────────────────┤
│ Page Directory (页目录) │
├────────────────────────────────────┤
│ Infimum + Supremum (最小最大记录) │
├────────────────────────────────────┤
│ User Records (用户数据) │
├────────────────────────────────────┤
│ Free Space (空闲空间) │
└────────────────────────────────────┘
关键参数
SQL
-- Buffer Pool大小(建议设为物理内存的60%-80%)
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
-- Buffer Pool实例数(大内存建议多实例减少竞争)
SHOW VARIABLES LIKE 'innodb_buffer_pool_instances';
-- 页大小
SHOW VARIABLES LIKE 'innodb_page_size';
改进的LRU算法
InnoDB对传统LRU算法进行了改进,解决全表扫描污染问题。
传统LRU问题
SQL
传统LRU问题:
全表扫描时,大量冷数据进入LRU头部
→ 热数据被挤出 → 缓存命中率下降
InnoDB改进LRU
将LRU链表分为两个区域:
SQL
┌─────────────────────────────────────────────────────┐
│ LRU链表 │
├─────────────────────┬───────────────────────────────┤
│ Young区域(热数据) │ Old区域(冷数据) │
│ (5/8) │ (3/8) │
│ 长期活跃数据 │ 新加载页/扫描页 │
└─────────────────────┴───────────────────────────────┘
↑ ↑
MRU端 LRU端
(最近最多使用) (最近最少使用)
页面访问流程
text
1. 读取页面
↓
2. 页面在Buffer Pool中?
├── 是 → 直接使用,移动到Young区头部
└── 否 → 从磁盘加载
↓
3. Free List有空闲页?
├── 是 → 直接分配
└── 否 → 淘汰LRU尾部页面
↓
4. 新页面进入Old区头部(非Young区)
↓
5. 再次访问该页面?
└── 是 → 移动到Young区头部(成为热数据)
关键参数
text
-- Old区域占比(默认3/8=37%)
SHOW VARIABLES LIKE 'innodb_old_blocks_pct';
-- 页面在Old区停留时间阈值(默认1000ms)
SHOW VARIABLES LIKE 'innodb_old_blocks_time';
-- 调整建议(避免全表扫描污染)
SET innodb_old_blocks_time = 1000;
-- 扫描期间设置更大的值,扫描后恢复默认
页面管理机制
三大链表
| 链表 | 作用 |
|---|---|
| Free List | 空闲页面链表,等待分配 |
| LRU List | 按访问时间排序的页面链表 |
| Flush List | 脏页链表,等待刷盘 |
页面状态
text
┌────────────────────────────────────┐
│ Page States │
├────────────┬───────────────────────┤
│ clean │ 干净页(未修改) │
├────────────┼───────────────────────┤
│ dirty │ 脏页(已修改,待刷盘) │
├────────────┼───────────────────────┤
│ free │ 空闲页(无数据) │
└────────────┴───────────────────────┘
脏页刷盘策略
text
-- 脏页刷盘策略
SHOW VARIABLES LIKE 'innodb_flush_method';
-- 后台刷盘线程数
SHOW VARIABLES LIKE 'innodb_page_cleaners';
-- 脏页占比阈值(触发刷盘)
SHOW VARIABLES LIKE 'innodb_max_dirty_pages_pct';
性能监控
text
-- 查看Buffer Pool状态
SHOW STATUS LIKE 'Innodb_buffer_pool%';
-- 关键指标
Innodb_buffer_pool_read_requests -- 逻辑读次数
Innodb_buffer_pool_reads -- 物理读次数(缓存未命中)
Innodb_buffer_pool_wait_free -- 等待空闲页次数
Innodb_buffer_pool_pages_dirty -- 脏页数量
-- 缓存命中率计算
命中率 = 1 - (Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests)
-- 目标:> 99%
监控查询
text
-- 查看Buffer Pool内部状态
SELECT
POOL_ID,
POOL_SIZE,
FREE_BUFFERS,
DATABASE_PAGES,
DIRTY_PAGES
FROM information_schema.INNODB_BUFFER_POOL_STATS;
调优建议
| 场景 | 建议 |
|---|---|
| 物理内存充足 | Buffer Pool设为70%-80%物理内存 |
| 高并发写入 | 增加innodb_buffer_pool_instances |
| 全表扫描频繁 | 增大innodb_old_blocks_time |
| 脏页刷盘慢 | 调整innodb_page_cleaners |
Buffer Pool大小调整需要重启服务,建议一次性设置合理值。
要点总结
- Buffer Pool是InnoDB核心缓存,存储数据页和索引页
- 采用改进LRU算法,分为Young区和Old区防止全表扫描污染
- 新页面先进入Old区,再次访问才进入Young区
- 监控缓存命中率,目标>99%
- 根据内存大小和业务场景调整Buffer Pool参数
📝 发现内容有误?点击此处直接编辑