[转]MySQL innodb buffer pool

最近在对公司的 MySQL 服务器做性能优化, 一直对 innodb 的内存使用方式不是很清楚, 乘这机会做点总结.

在配置 MySQL 的时候, 一般都会需要设置 innodb_buffer_pool_size, 在将 MySQL 设置在单独的服务器上时, 一般会设置为物理内存的80%.

之前一直疑惑 MySQL 是怎么缓存数据的(不是指query cache), 直觉应该是LRU, 但如果 query 一下从磁盘上读取大量的数据的话(全表扫描或是 mysqldump), 是不是很容易就会把热数据给踢出去?

Buffer Pool 的 LRU 机制

innodb buffer pool 在内存中其实是一个 list, 维护数据的确是用 LRU, 但是做了点优化.

buffer pool 将数据切割成两块, 头部是 young page list, 尾部是 old page list. 两者的空间比例默认是5: 3, 可以通过 show variables like “innodb_old_blocks_pct” 查看, old block的比例即为 37%.

当数据第一次被从磁盘上读取, 要加入 buffer pool 时, 会先放在 old page list 的头部, 此时如果 old page list 满了,则会把它尾部的数据踢出去. 当数据在buffer pool中被再次访问的时候, 如果它在 old page list 中, 则会被转移到 young page list 的头部(如果数据已经在 young page list 中, 则只有它和头结点的距离超过一定长度时才会被转移到头部).

通过这种机制, 可以有效得避免临时的大量数据读取冲刷掉 hot page, 这些一次性的数据如果没有被访问的话,很快会被从old page list 中踢出去.

查看 buffer pool 的使用状况

使用 show engine innodb status, 可以获取 innodb 的运行信息, 查看其中 BUFFER POOL AND MEMORY 一节, 可以看到 innodb buffer的使用情况.

示例:

BUFFER POOL AND MEMORY
----------------------
Total memory allocated 11948523520; in additional pool allocated 0
Dictionary memory allocated 881470
Buffer pool size 712576
Free buffers 7638
Database pages 676994
Old database pages 250018
Modified db pages 22961
Pending reads 4
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 86737875, not young 2068799422
188.89 youngs/s, 5482.68 non-youngs/s
Pages read 390735994, created 1284180, written 17312013
972.19 reads/s, 0.30 creates/s, 36.63 writes/s
Buffer pool hit rate 975 / 1000, young-making rate 4 / 1000 not 141 / 1000
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 676994, unzip_LRU len: 0
I/O sum[374688]:cur[1688], unzip sum[0]:cur[0]

几个需要注意的参数:

Total memory allocated: innodb 已分配的总内存数, 单位 bytes

Buffer pool size: buffer pool 大小, 单位是 pages, MySQL 的 page 默认是 16KB, 这里 pool 的总大小就是 10G 多.

Free buffers: 还没使用的 buffer size, 单位也是 pages.

Database pages: 总共已使用的 page 数目.

Old database pages: old page list 中的 page 数目.

youngs/s: 每秒对 old page list 的访问次数(不是page的个数), 这种访问会就会把page从 old list 里移动到 new page list 的头部.

evicted without access: 放进 buffer pool 一次都没访问过就被踢出去的 page 数目. 如果发生了全表扫描, 这个数值可能就会临时变高.

补充

因为多线程对 buffer pool 进行访问的时候会发生资源竞争, MySQL 支持对 buffer pool 进行 sharding, 从而提高并发性能, 参数是innodb_buffer_pool_instances.

每个 instance 的 size = innodb_buffer_pool_size/innodb_buffer_pool_instances

官方推荐 sharding 后单个 instance 的 size 不要小于 1G*B. 比如我现在 pool 的总 size 是10 GB, 我设置的 instance 个数就是8.

上面 engine status 只能看到 buffer pool 的汇总情况, 如果想看每个 instance 的详情,
可以从 performance_schema 里拿:

SELECT * FROM INFORMATION_SCHEMA.INNODB_BUFFER_POOL_STATS

References:

https://dev.mysql.com/doc/refman/5.6/en/innodb-buffer-pool.html
https://dev.mysql.com/doc/refman/5.6/en/innodb-performance-midpoint_insertion.html
https://dev.mysql.com/doc/refman/5.6/en/innodb-buffer-pool-monitoring.html
https://dev.mysql.com/doc/refman/5.6/en/innodb-multiple-buffer-pools.html

转载于:https://www.cnblogs.com/sunss/p/6122075.html

<think>我们正在讨论MySQL InnoDB Buffer Pool的淘汰机制。根据引用[2]和引用[3],我们知道Buffer Pool使用了一种基于LRU(最近最少使用)算法的淘汰机制,但进行了优化,以避免全表扫描等操作导致的热数据被淘汰的问题。具体来说,InnoDB将LRU链表分为两个部分:新生代(new sublist)和老生代(old sublist)。默认情况下,新生代占5/8,老生代占3/8。 当新页面读入Buffer Pool时,它首先被插入到老生代的头部(即整个LRU链表的5/8位置,也就是老生代的头部)。这样,全表扫描等操作一次性加载的大量数据会首先进入老生代,而不会直接进入新生代,从而避免它们立即淘汰新生代中的热点数据。 当页面被访问时,如果该页面已经在老生代中,并且在其被加载后存活了一段时间(通过参数`innodb_old_blocks_time`设置,默认1000毫秒),那么再次访问该页面时,它会被移动到新生代的头部。如果在设定的时间内被再次访问,则不会移动,因为可能只是临时访问(如全表扫描)。 淘汰过程发生在当Buffer Pool需要空间来加载新页面时。InnoDB会从LRU链表的尾部(即老生代的尾部)开始淘汰页面。如果老生代的页面被修改过(成为脏页),则需要先将其刷新到磁盘(由后台线程完成)才能被淘汰。 此外,Buffer Pool还通过其他链表管理,如free链表(记录空闲缓存页)、flush链表(记录脏页)等,但淘汰机制主要基于改进的LRU链表。 现在,我们根据这些信息来回答用户的问题。</think>### MySQL InnoDB Buffer Pool 淘汰机制工作原理 MySQL InnoDB Buffer Pool 的淘汰机制采用**改进型LRU算法**(Least Recently Used,最近最少使用),通过多层链表结构和冷热数据分离策略优化传统LRU算法,避免全表扫描等操作污染热数据。具体工作原理如下: #### 一、核心数据结构 1. **LRU链表**(核心淘汰控制) - 分为两个子链表: - **新生代(New Sublist)**:存放热点数据,占5/8空间 - **老生代(Old Sublist)**:存放新加载或低频数据,占3/8空间 - 链表头部为最近访问数据,尾部为待淘汰数据 2. **Free链表** - 管理空闲缓存页,当需要加载新页时从此链表获取空间 3. **Flush链表** - 记录被修改过的脏页(需刷盘) #### 二、淘汰流程 1. **新数据插入** - 新页首次加载时插入老生代头部(非LRU头部) ```mermaid graph LR A[磁盘数据页] --> B{Buffer Pool空间} B -->|有空闲页| C[从Free链表取页] B -->|无空闲页| D[触发淘汰] D --> E[淘汰老生代尾部页] E --> F[新页插入老生代头部] ``` 2. **数据访问规则** - 访问老生代数据时: - 若数据在内存中停留时间 < `innodb_old_blocks_time`(默认1000ms),保持原位 - 若停留时间 ≥ 该阈值,则移至新生代头部 - 访问新生代数据时直接移至新生代头部 3. **淘汰触发条件** - 当Free链表无空闲页时 - 淘汰顺序: 1. 从老生代尾部选取未修改的干净页直接淘汰 2. 若老生代无干净页,则淘汰新生代尾部页 3. 若需淘汰脏页,需先加入Flush链表由后台线程刷盘 #### 三、关键优化设计 1. **冷热隔离** - 全表扫描加载的数据仅存在老生代,避免冲击热数据 - 通过`innodb_old_blocks_pct`调整老生代比例(默认37%) 2. **时间窗口保护** - `innodb_old_blocks_time`参数建立时间屏障,防止短期访问污染新生代 3. **批量淘汰** - 每次淘汰触发时清理多个页面(避免频繁淘汰影响性能) #### 四、参数配置示例 ```sql -- 查看关键参数 SHOW VARIABLES LIKE 'innodb_old_blocks_pct'; -- 老生代占比 SHOW VARIABLES LIKE 'innodb_old_blocks_time'; -- 冷数据保护时间(ms) ``` > **设计意义**:此机制有效平衡了高频访问数据的留存与突发数据加载的影响,在TPCC测试中可使查询性能提升10倍以上[^2][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值