https://juejin.im/book/5bffcbc9f265da614b11b731/section/5c238f0851882521eb44c51f
Buffer Pool:
mysql数据库启动的时候, 就向操作系统申请了一片连续的内存, 以后读/写访问以后,把页缓存在里面起来, 下次如果命中了就可以省了最昂贵的磁盘IO了
我们设innodb_buffer_pool_size
来控制这块内存的上限
控制块
每个缓存页,都有对应的一个控制信息, 叫控制块,
控制块从Buffer Pool的前面往后存, 缓存页从后往前存
我们设置的
innodb_buffer_pool_size
不包括这些控制块的大小,操作系统会自动多给一些内存(大概5%)放控制块
剩下放不下一页了就是碎片
控制信息包括该页所属的表空间编号、页号、缓存页在Buffer Pool中的地址、链表节点信息、一些锁信息以及LSN信息(锁和LSN我们之后会具体唠叨,现在可以先忽略),当然还有一些别的控制信息
free链表
空闲的页对应的控制块会形成一个双向链表,
刚开始所有的控制块都在这个free链表里面,需要用缓存一页就从free里面得到还空着的缓存页,就把对应的控制块从free链表移除移除,
这个描述链表信息(头/尾/长度)的 就是 基节点
不包括在 Buffer Pool 里面
flush链表
和free链表其他都一样, 只是存的是 改写过的页
这些页以后要刷回磁盘的
用来取缓存页的hash表
key: 表空间号+页号
value: 缓存页
这样知道表空间号+页号 就能找到对应缓存页了
LRU链表
Least Recently Used
这个链表按比例(可动态设置), 分成2部分 前一部分是 确定常用的页
- 热数据/young区域: 确定使用频率很高的缓存页,
- 冷数据/old区域: 使用频率不一定高的缓存页
第一次访问的时候, 先放到冷数据,如果在短时间内又被访问了, 就升级到热数据
这是为了抑制
预读(猜测附近的页/同区也会需要所以都先加载了),
和
全部扫描
造成的劣币驱逐良币
刷 脏页 到硬盘
后台有专门的线程定时把脏页刷到硬盘
- 从
LRU 链表
的冷数据 尾部 - 从
flush链表
如果, 需要加载一页到缓存
Buffer Pool 都已经满了,那么
-
LRU 链表
尾部 没有没修改过的, 覆盖掉 - 还是不够, 只能把
LRU 链表
尾部的脏页先刷, 这样就不是在后台线程做,而是在用户线程做, 严重降低处理速度了
把 Buffer Pool 拆成多个
多线程环境下, 没有线程要访问 Buffer Pool 都要改控制块连成的链表, 只能都加锁,并发很高的情况下 会影响请求速度
可以把它们拆分成若干个小的Buffer Pool,每个Buffer Pool都称为一个实例,它们都是独立的
实例们平分Buffer Pool 设置的大小
5.7.5 以后 为了运行调整 Buffer Pool 大小的改进
在Buffer Pool 实例内部 以
chunk
为单位来使用缓存, 一个chunk
才是连续的内存, chunk
之间不连续
每个
chunk
多大, 运行前配置好, 启动以后不能改了