InnoDB的Buffer Pool
缓存的重要性
由于磁盘和内存之间的读写速度差距,当需要访问某个页中的数据的时候,就会把完整的页加载到内存中,在页被使用完之后,并不急着把页对应的内存空间释放掉,而是将页缓存起来,以待下次访问,就可以省去磁盘IO了
什么是Buffer Pool?
为了缓存磁盘中的页,MySQL在内存中开辟了一片连续的空间,叫做Buffer Pool,Buffer Pool中缓存页的大小也为16k,为了更好的管理这些缓存页,MySQL为每个缓存页创建了一个控制块,每个控制块记录着该页一些控制信息(表空间编号,页号,缓存页在Buffer Pool中的地址,链表节点信息等)

free链表
Buffer Pool是用来缓存页的,MySQL运行过程中,会将页加载到内存中,怎么知道加载进来的页该放在哪个位置呢?怎么知道哪块内存空间是空闲的呢?
可以使用链表来进行管理,MySQL刚启动的时候,所有的缓存页都是空闲的,可以将所有的空闲缓存页的控制块由一个链表串联起来,称为free链表,当从磁盘加载页的时候,可以从free链表中找到一个空闲的控制块,然后把磁盘中页的信息填写到控制块中,然后将这个控制块从free链表中移除,即代表缓存页已分配给该页
缓存页的hash处理
Buffer Pool中有这么多的缓存页,当我们要访问我们需要的页的时候,怎么知道它在不在Buffer Pool中呢?可以通过哈希表来进行管理:表空间编号+页号作为key,缓存页作为value,如果在Buffer Pool中找到需要的缓存页则直接使用,否则将磁盘页加载到Buffer Pool中
flush链表
当修改某个缓存页之后,不是立即刷盘的,否则会产生大量的IO,而是等到一个合适的时间刷盘,但是如果不立即刷盘,怎么知道哪些页是脏页呢?可以创建一个管理脏页的链表,称为flush链表,flush链表由脏页的控制块串联而成
划分区域的LRU链表
Buffer Pool是内存空间,内存容量是有限的,所以当空间不够的时候,就要淘汰掉某些页,可以使用LRU算法来进行管理,但是普通的LRU算法肯定是不行的,因为会存在以下问题:
InnoDB提供了预读功能,InnoDB认为当前请求可能之后会读取某些页面,就会预先把它加载到Buffer Pool
线性预读
如果顺序访问某个区的页面超过了某个阈值,就会触发线性预读,异步的读取下一个区中的全部页面到Buffer Pool中
随机预读
如果Buffer Pool中已经缓存了某个区中13个连续的页面,会触发随机预读,读取本区的所有页面到Buffer Pool中
如果这些预读的页用到了,能够提高效率,如果没有用到,这些预读的页都会放到LRU链表头部,那么尾部的页很快就会被淘汰,造成了劣币驱逐良币的现象,降低了缓存命中率
全表扫描:全表扫描会访问该表所在的所有的页,那么这些页都会被读取到Buffer Pool中,如果表很大,则页很多,那么Buffer Pool相当于一次大换血,严重影响了其他请求对Buffer Pool的使用,降低了缓存命中率
所以MySQL将LRU链表划分了区域,前面一部分为热数据,称为young区域,后面一部分为冷数据,称为old区域,old区域大约占3/8,并且针对上述情况进行优化LRU算法:
针对预读的页面可能不被访问
当磁盘页初次加载到Buffer Pool中,该页的控制块会被放到old区域头部,减少对young区域中热数据的影响
针对全表扫描,短时间访问大量使用率低的页
在第一次访问某个old区域的页时,在它的控制块中记录访问时间,如果后续的访问的时间在某个时间间隔内,该页面的控制块不会被移到young区域
