16.缓冲池-BufferPool
buffer pool
常识:
- Buffer Pool
是一片
连续的内存空间,通过innodb_buffer_pool_size在服务器运行过程中调整buffer pool大小,默认为128MB Free Page(空闲页),此页未被使用,位于Free 链表;Clean Page(干净页),此页已被使用,但是页面未发生修改,位于LRU 链表。Dirty Page(脏页),表示此页已被使用且已经被修改,其数据和磁盘上的数据已经不一致。- 当
脏页上的数据写入磁盘后,内存数据和磁盘数据一致,那么该页就变成了干净页。脏页同时存在于LRU链表和Flush链表。 提高读性能: 读取数据的时候,先从buffer pool LRU链表(干净页)读取数据,如果没有从磁盘读取并把它相邻的数据页一并加载进来。提高写性能: 更新数据的时候,不需要每次都要写入磁盘,而是将 Buffer Pool 对应的缓存页标记为脏页,然后再由后台线程将脏页写入到磁盘
buffer pool的作用:
- 本质上就是个缓存池。为了降低磁盘和内存的IO差异,提高数据库的读写性能而设计
- 存储的数据包括:
索引页数据页undo页插入缓冲区change buffer自适应哈希索引锁其他
结构:
* 基本构成: * 控制块: *
存储信息:缓存页的表空间、页号、缓存页地址、链表节点 *
控制块跟缓存页一一对应 * 缓存页: *
大小16KB,跟页的大小一致
管理空闲页:
- 如何查找空闲页?
- 当需要从磁盘中加载一个页到 Buffer Pool
中时,通过
Free链表,取一个空闲的缓存页,并且填写该缓存页对应的控制块的信息,然后把该缓存页对应的控制块从Free 链表中移除
- 当需要从磁盘中加载一个页到 Buffer Pool
中时,通过
管理脏页:
- 如何查找脏页并写入磁盘?
- 在需要刷盘时,使用
Flush链表,后台线程就可以遍历脏页,写入到磁盘
脏页刷盘时机:- 核心:既要保证缓存、磁盘的一致性,又要确保刷盘的效率
空闲时后台线程定期将适量的脏页刷入到磁盘:- BUF_FLUSH_LRU方式:
定时从LRU链表``尾部扫描脏页刷新到磁盘 - BUF_FLUSH_LIST方式:
定时从Flush链表刷新部分脏页到磁盘
- BUF_FLUSH_LRU方式:
- Mysql
服务关闭前,会把所有的脏页刷入到磁盘 Buffer Pool 空间不足时,如果需要淘汰脏页,此时会先将脏页刷入磁盘,再进行淘汰redo log 日志满了的情况下,会触发脏页刷入磁盘
- 核心:既要保证缓存、磁盘的一致性,又要确保刷盘的效率
缓存淘汰和命中率:
- 基于
LRU(Least recently used) - 如何管理缓存页的过期,提高命中率
- 缓存相关的问题
- 避免预读导致的缓存失效
- 预读:在加载数据页时,为了减少磁盘IO,会提前把它相邻的数据页一并加载进来
- 失效:预读数据占据了缓存头部,一直未被访问,一些可能访问更频繁缓存反而被迫淘汰,导致失效
- 解决方案:
- 区分
预读数据和真正被访问到的数据,分别存到old区和young区 - 进入
young区新增条件:在 old 区域被真正访问过
- 区分
- 具体方法:
young区:在LRU前部分,缓存时间更长,占63%old区:在LRU后部分,缓存时间相对短,占37%- 通过
innodb_old_blocks_pct设置young区和old区占比 - 这样如果预读的页就只加入到
old区域的头部,当页被真正访问的时候,才将页插入 young 区域的头部,否则会被优先淘汰
- 避免Buffer Pool污染
- 污染的含义:扫描了大量的数据,导致全部页被替换,大量热数据被淘汰
- 触发:
- 结果集比较离散,大量的页被访问
- 全表扫描,所有页被访问
- 解决方案:
- 进入
young区新增条件:在 old 区域停留时间超过 1 秒 - 在
控制块中记录该页第一次被真正访问的时间 - 通过
innodb_old_blocks_time设置停留时间
- 进入
- 具体方法:
- 当前访问时间与控制块中该页
第一次被访问的时间相比- 大于1s, 则可以移动到
young区域头部 - 小于1s,则继续停留在
old区域
- 大于1s, 则可以移动到
- 当前访问时间与控制块中该页
- 其他优化:
- 防止
young区域内部移动过于频繁- 处于
young区域前1/4的数据被访问时,不会移动到头部 - 处于
young区域后3/4的数据被访问时,移动到头部
- 处于
- 防止
- 避免预读导致的缓存失效
Buffer Pool的其他优化:
多实例:
- 在
Buffer Pool特别大而且多线程并发访问特别高的情况下,单一的Buffer Pool可能会影响请求的处理速度。(多线程下,访问Buffer Pool中的各种链表都需要加锁) - 多实例解决了锁的粒度问题

以chunk为单位申请内存:
- 在重新调整
Buffer Pool大小的时候,需要重新向操作系统申请一块连续的内存空间,然后将旧的Buffer Pool中的内容复制到这一块新空间,这个操作极其耗时 - 改为以
chunk为单位向操作系统申请连续内存,这样一个Buffer Pool实例其实是由若干个chunk组成的。调整大小时,只需要申请新的chunk或者减少chunk即可
- 参数配置:
innodb_buffer_pool_chunk_size,默认为128M。只能在服务器启动时指定,在服务器运行过程中是不可以修改的
Change buffer/Insert buffer(插入缓冲区)
- 作用:避免频繁磁盘IO
- 触发时机:
- 当对
非唯一索引进行插入、更新或删除操作时: - 如果相关的索引页不在
Buffer Pool中,InnoDB会将这些操作暂存在插入缓冲区;等到系统有空闲资源时,InnoDB会将插入缓冲区中的操作应用到Buffer Pool中的索引页。 - 如果相关索引页在
Buffer Pool中,则直接更新Buffer Pool
- 当对
- 注意点:只针对
非唯一索引生效,原因:唯一索引有唯一性约束,必须要从磁盘取出数据才能校验唯一性是否正确;非唯一索引没有这个限制,所以可以这么做

浙公网安备 33010602011771号