Buffer Pool
Buffer Pool是mysql在Innodb存储引擎下操作数据的一个缓冲内存区域,为了加速对数据增删改查,mysql实际主要就是对Buffer Pool中的数据缓存页进行一系列操作,空间默认128M。也可以适当的调大这一块内存,以获得更高一点的性能。
mysql执行sql的时候,根据指定索引找到数据页,加载到Buffer Pool中。mysql基本的数据存储单元就是数据页,数据页中有索引和行记录,默认16kb
这个就是B+树落在叶子节点具体化的数据存储页,当然只是相对于InnoDB这种存储引擎来说的。
注意,加载到Buffer Pool并不是按需加载,而是一次性把整个数据页全部加载进去,甚至会把相邻的数据页也加载进去,这样便可省去后续的磁盘IO,提升效率。这就是所谓的‘预读极致’。
Buffer Pool如何管理缓存页
free链表
Buffer Pool内维护了一个free链表,用以管理哪些空闲的内存块没有缓存页,从磁盘加载数据页,首先会从free链表内从头部找到链表节点,那里记录了可用空闲内存块的地址,缓存页便往那里填充,再把对应的链表节点删除
flush链表
Buffer Pool内部同时页维护了一个flush链表,这个是用来描述那些缓存页被更新过,更新过的脏缓存页会从尾部追加到这个链表中。
LRU链表
Buffer Pool还维护了一个很重要的LRU链表,它定义了一种在Buffer Pool中缓存页区域满了的情况下,如何对某些缓存页进行淘汰的策略
一般LRU算法都是将热点数据往链表头部移动,等到需要执行LRU的时候就删除尾部不常用的数据。
这里的LRU链表设计成了两部分,分为热数据区域和冷数据区域,比例5:3
第一次加载数据缓存页的时候,缓存页信息被放入冷数据区域的头部
数据页加载到缓存页,超过1秒后读取这个缓存页,这一页会移动到热数据区域头部。如果在1秒以内访问该缓存页,就不会移动。当缓存页全满了以后,就会把LRU链表中冷数据区域的节点清除,相应加入free链表中
为啥这么设计呢?主要有两个问题:
1、预读失效。就是开启预读以后把磁盘中相邻连续的缓存页都加载了进了Buffer Pool,但是后面有又没有对这些数据页进行读取,所以需要让那些预读失效的缓存页尽可能在短时间内被淘汰,让真正被读取到的热点缓存页能够存活久一点
2、全面扫描导致缓存页换血。可能由于错误的操作,导致需要全表扫描大量数据导致Buffer Pool中的缓存页全部被替换(“换血”),让一些热点数据也被换出去了,从而降低mysql的性能
这样就可以见识到Innodb如此设计LRU的好处了,虽然也有可能造成误杀,但大部分情况下还是可以保护到真正的热点数据缓存页,提升性能。