MySQL缓冲池Buffer Pool!
MySQL缓冲池Buffer Pool!
月伴飞鱼
Buffer Pool
作为缓存页。
MySQL
数据以页为单位,每页默认16KB
,称为数据页,在Buffer Pool
里面会划分出若干个缓存页与数据页对应。缓存页的元数据信息(描述数据):
- 它与缓存页一一对应,包含一些所属表空间、数据页的编号、
Buffer Pool
中的地址等。后续对数据的增删改查都在
Buffer Pool
里操作:
- 查询:从磁盘加载到缓存,后续直接查缓存。
- 插入:直接写入缓存。
- 更新删除:缓存中存在直接更新,不存在加载数据页到缓存更新。
缓存页哈希表:
如何在
Buffer Pool
里快速定位到对应的缓存页?
使用哈希表来缓存它们间的映射关系,时间复杂度是
O(1)
。表空间号+数据页号,作为一个
key
,缓存页的地址作为value
。每次加载数据页到空闲缓存页时,就写入一条映射关系到缓存页哈希表中。
后续的查询,就可以通过缓存页哈希表路由定位了。
Free链表:
使用链表结构,把空闲缓存页的描述数据放入链表中,这个链表称为
free
链表。
- 往描述数据与缓存页写入数据后,就将该描述数据移出
free
链表。
Flush链表:
如果修改了Buffer Pool中某个缓冲页的数据,那么它就与磁盘上的页不一致了,这样的缓冲页也被称之为脏页。
创建一个存储脏页的链表,凡是被修改过的缓冲页对应的控制块都会作为节点加入到这个链表中。
- 该链表也被称为Flush链表。
后续异步线程都从
flush
链表刷缓存页,当Buffer Pool
内存不足时,也会优先刷Flush
链表里的缓存页。
LRU链表:
借鉴
LRU
算法思想,把最少使用的缓存页淘汰(命中率低),提供LRU
链表出来。
- 当
free
链表为空的时候,直接淘汰LRU
链表尾部缓存页即可。
LRU不足
磁盘预读:
如果按照简单LRU的思路实现内存淘汰,可能会导致部分真正的热点数据被预读的数据淘汰掉。
而预读的数据又不一定被使用到,之后缓存命中率就会下降。
全表扫描:
全表扫描的过程其实也会不断的把数据页加载到Buffer Pool中。
比如数据库备份时,就会把缓存中原有的热点数据淘汰,最终降低缓存命中率。
冷热数据分离设计
给
LRU
链表做冷热数据分离设计,把LRU
链表按一定比例,分为冷热区域,热区域称为young
区域,冷区域称为old
区域。数据页第一次加载进缓存页的时候,是先放入冷数据区域的头部,如果1秒后再次访问缓存页,则会移动到热区域的头部。
这样就保证了预读机制与全表扫描加载的数据都在链表队尾。