Buffer Pool🚩
作用:
- 缓存索引页,数据页,提高查询性能
- 更新记录,先缓存到buffer pool(Change Buffer),而不是直接写回磁盘,提高效率
Buffer Pool有多大
innodb_buffer_pool_size 参数用于设置 Buffer Pool 的大小,默认128MB
一般建议设置成可用物理内存的 60%~80%
Buffer Pool被划分成缓存页,默认一页16KB
InnoDB 为每一个缓存页都创建了一个控制块,控制块信息包括「缓存页的表空间、页号、缓存页地址、链表节点」等等
Change Buffer
Change Buffer写缓冲。
Change Buffer默认占Buffer Pool的25%,最大设置占用50%:
Change Buffer是在【非唯一普通索引页】不在buffer pool中时,当对页进行了写操作时,在不影响数据一致性的前提下。InnoDB会将数据先写入Change Buffer中,等未来数据被读取时,再将 change buffer 中的操作merge到原数据页中。
也就是如果buffer pool有数据,更新buffer pool;没有的话,也不查磁盘数据,而是写入Change Buffer。通过merge操作持久化。
change buffer的使用场景
读多写少的场景,Change Buffer很有用,因为一次merge可以处理很多读操作。
而写完了立刻要查,那就会立即触发merge,也是对磁盘的读写,这样随机访问IO的次数不会减少,反而增加了change buffer的维护代价,起到了副作用。
merge触发时机
- 读取Change buffer中记录的数据页时,会将Change buffer合并到buffer Pool 中,然后被刷新到磁盘。
- 当系统空闲或者slow shutdown时,后台master线程发起merge。
- change buffer的内存空间用完了,后台master线程会发起merge。
- redo log写满了,但是一般不会发生。
buffer pool的架构
分为三个链表:
- Flush 链表 用于快速找到脏页数据,节点是控制块
- Free 链表 用于快速找到空闲的缓存页
- LRU链表 用于存放干净页
LRU的实现
LRU链表被分为两部分,前半部分被称为young 区域,后半部分被称为old 区域。
innodb_old_blocks_pc控制old区域占比,默认37%
预读的页加入到 old 区域的头部
被真正访问才插入到young区域的头部
这解决了预读失效问题
还需要提高进入young链表的门槛
只有同时满足「被访问」与「在 old 区域停留时间超过 x 秒」两个条件,才会被插入到 young 区域头部,x是由innodb_old_blocks_time参数控制的,默认1000ms
这解决了缓存污染问题
这个和操作系统的内存管理差不多,都是对LRU算法的改进,问题相同,解决方案不同
另外young 区域前面 1/4 被访问不会移动到链表头部,只有后面的 3/4被访问才会
脏页什么时候会被刷入磁盘?
不用担心,靠WAL技术和redo log日志保证