MySQL - 存储引擎之Innodb内存结构
InnoDB存储结构
- MySQL官网存储引擎架构图和介绍
https://dev.mysql.com/doc/refman/8.0/en/innodb-architecture.html
根据架构图可以看出,Innodb主要分为内存结构与磁盘结构两部分
Innodb内存结构
Buffer Pool(缓冲池)
Buffer Poll 按照Page为存储单位进行存储,大小默认16KB,采用链表结构来管理Page(页),Page大概分为三种类型:
- free page:没有被使用的page
- clean page:被使用的page,但是数据没有被修改过(缓存数据与磁盘数据一致)
- dirty page:被使用过的page(脏页,缓存中的数据和磁盘中的数据不一致)
针对上述三种类型,Innodb通过3种链表结构来维护管理:
1. Free List(空闲链表)
- 作用:管理缓冲池中年未被使用的空闲页
- 数据结构:双向链表(一个节点有两个指针域)
- 工作流程:
- 初始化时候,所有页加入Free List
- 需要读取新页时:
- 从Free List头部获取空闲页
- 加载磁盘数据到该页
- 页从Free List移除
- 当页被释放时候,重新加入Free List尾部
- 关键点:
- 缓冲池的空闲页"储备库"
- 加载磁盘数据到该页
SHOW ENGINE INNODB STATUS;
中的Buffer pool size
包含Free List页数- 当Free List为空时触发页淘汰
2. Flush List(刷新链表)
- 作用:管理需要写入磁盘的脏页(被修改的页)
- 数据结构:按最早修改时间(oldest_modification)排序的双向链表
- 工作流程:
- 页首次被修改时,加入Flush List尾部
- 后台刷新线程:
- 从Flush List头部获取最老的脏页
- 异步写入磁盘
- 写入成功后从链表移除
- 关键点:
- 按LSN(Log Sequence Number)排序
- 检查点机制基于Flush List工作
innodb_io_capacity
控制刷新速率SHOW STATUS LIKE 'Innodb_buffer_pool_pages_dirty'
查看脏页数量
3. LRU List(最近最少使用链表)
- 作用:管理已使用的内存页,实现页淘汰算法
- 数据结构:改进的LRU双向链表(分New和Old区)
- 工作流程:
- 新页加载到Midpoint位置(Old区头部)
- 页被访问时:
- 若在Old区且存活超过
innodb_old_blocks_time
,移至Young区头部 - 在New区,移至链表头部
- 淘汰时从尾部移除Old区页
- 关键优化:
- Midpoint Insertion:避免全表扫描污染缓存
- Old Sublist:
innodb_old_blocks_pct
控制比例(默认37%) - reeze Period:
innodb_old_blocks_time
(默认1000ms)防止短期访问提升
普通LRU:末尾淘汰法,新数据从链表头部加入,释放空间时从末尾淘汰
改进LRU:链表分为New和Old两个部分,加入元素时不是从表头插入,而是从中间Midpoint位置插入(如果数据很快被访问,数据就向New头部移动,如果数据被访问很慢,就向Ol尾部移动等待淘汰)
主要解决:预读失效和Buffer Pool污染问题
链表整体协同工作流程图:
监控命令:
-- 查看链表状态
SHOW ENGINE INNODB STATUS\G
-- 查看页分配
SELECT
POOL_ID,
FREE_BUFFERS AS `Free List`,
DATABASE_PAGES AS `LRU List`,
OLD_DATABASE_PAGES AS `LRU Old区`,
MODIFIED_DATABASE_PAGES AS `Flush List`
FROM information_schema.INNODB_BUFFER_POOL_STATS;
重点参数介绍:
参数 | 默认值 | 作用 |
---|---|---|
innodb_page_size | 16384 | page页大小(字节) |
innodb_old_blocks_pct | 37 | Old区占LRU比例(%) |
innodb_old_blocks_time | 1000 | Old区晋升等待时间(ms) |
innodb_max_dirty_pages_pct | 90 | 最大脏页比例(%) |
innodb_io_capacity | 200 | 每秒刷新页数上限 |
innodb_buffer_pool_chunk_size | 134217728 | 每个实例的数据大小 |
innodb_buffer_pool_instances | 1 | Buffer Pool实例数 |
innodb_buffer_pool_size | 134217728 | 总数据大小 |
Adaptive Hash Index(自适应哈希索引)
自适应哈希索引(AHI)是Innodb引擎内置的自动内存优化机制,通过监控查询模式,在内存中动态创建哈希索引来加速B+树查询
性能提升原理:
- O(1)访问速度:哈希索引 vs B+树的 O(log N)
- 减少B+树遍历:避免多级节点访问
- 降低CPU消耗:简化查询处理路径
- 自动优化:无需DBA手动干预
性能对比:
操作 | B+树索引 | AHI启用 | 提升幅度 |
---|---|---|---|
等值查询 | 3-4次页访问 | 1次内存访问 | 5-8倍 |
高并发点查 | 大量树遍历 | 直接哈希定位 | 10倍+ |
热点数据访问 | 重复遍历路径 | 一次哈希命中 | 3-5倍 |
Change Buffer(写缓冲区)
Change Buffer是Innodb用于优化非唯一,二级索引写操作的组件,通过延迟写入操作来提升数据库性能
性能提升原理:
- 减少随机I/O:合并多个变更到单词磁盘写入
- 降低磁盘压力:避免即时写入索引页
- 提高相应速度:写操作无需等待磁盘I/O
性能对比:
场景 | 无Change Buffer | 启用Change Buffer | 提升幅度 |
---|---|---|---|
批量INSERT | 每次索引更新都需磁盘I/O | 仅需1次合并写入 | 5-10倍 |
高并发UPDATE | 大量随机写操作 | 批量顺序写入 | 3-8倍 |
DELETE操作 | 即时更新索引结构 | 延迟合并删除标记 | 2-5倍 |
工作原理:
- 写入阶段
- 合并阶段
- 读取触发:当首次读取索引页时
- 后台合并:
innodb_background_merge_threads
线程定期处理 - 关闭数据库:系统关闭时强制合并
配置参数:
参数 | 默认值 | 说明 |
---|---|---|
innodb_change_buffer_max_size | 25% | Change Buffer最大内存占比 |
innodb_change_buffering | all | 缓冲操作类型 |
innodb_background_merge_threads | 4 | 后台合并线程数 |
工作原理:
- 创建机制
- 哈希结构
+---------------------+
| 哈希键 |
|(索引值 + 页号) | => 指向缓冲池中的页
+---------------------+
配置参数:
参数 | 默认值 | 说明 |
---|---|---|
innodb_adaptive_hash_index | ON | 是否启用AHI |
innodb_adaptive_hash_index_parts | 8 | AHI分区数 |
innodb_adaptive_hash_index_threshold | 100 | 触发创建阈值 |
Log Buffer(日志缓
Log Buffer是Innodb引擎的核心内存组件,用于临时存储重做日志(Redo Log)条目,作为磁盘日志文件与内存操作之间的高性能
性能提升原理:
- 批量写入:合并多次小I/O为单次大I/O
- 顺序写入优化:即使随机更新,日志仍顺序写入
- 异步提交:事务可先返回成功在持久化日志
- 减少磁盘压力:避免每次事务都直接写盘
工作流程:
- 写入路径
关键配置参数:
参数 | 默认值 | 说明 |
---|---|---|
innodb_log_buffer_size | 16MB | 缓冲区大小 |
innodb_flush_log_at_trx_commit | 1 | 刷盘策略 |
innodb_flush_log_at_timeout | 1s | 后台刷盘间隔 |
刷盘策略详解:
策略值 | 名称 | 持久性 | 性能 | 适用场景 |
---|---|---|---|---|
1 | 每次提交刷盘 | 最高(ACID) | 最低 | 金融交易系统 |
2 | 提交只写OS缓存 | 中等(OS崩溃可能丢数据) | 高 | 常规业务系统 |
0 | 每秒刷盘 | 最低(崩溃丢1s数据) | 最高 | 日志采集等 |