Buffer pool 是一片连续的内存,由缓冲页组成, 缓冲页与表空间的页面都是16KB,通过一系列控制块来管理。
free链表 没有使用的缓冲页在 free链表中
不仅free,其他所有链表都是管理的缓冲页的控制块,并且链表的基节点不算在Buffer Pool大小之内,但实际分配的内存会大于Buffer Pool大小。
当访问某个数据页的时候,会从free链表上取下来一个空闲的缓冲页,将磁盘的页加载到该缓冲页。
可以通过 表空间+页号 作为key 缓冲页控制块作为 value 快速访问缓冲页 —— hash表。
当缓冲页的数据被修改了,会将它同时加入到flush链表中。
为了提高Buffer Pool的命中率,使用LRU链表来管理缓冲页。
但是InnoDb有预读机制
- 如果顺序访问某个区的56个页面,就会把下一个区全部页面异步加入Buffer Pool。
- 如果随机读取到本区13个连续的页面( 1 /4 young),会把本区其他页面加入Buffer Pool。
存在的问题
- 读入Buffer Pool的页面不一定用得到(预读)
- 同时全表扫描将使用频率低的页面加载进来,将使用频率额高的页面淘汰出去
LRU 被设置为 3/8 young(热数据区) 5/8 old区,预读页面初次加载会链接在old区域,后续会被淘汰。同时为了应对全表扫描,连续访问一个页将会old区页面加载到young,设置了访问间隔大于1秒才将old页会加入young区域。
为避免每次访问young区都会将页面移动到链表头,设置了大于young 1/4,不会移动到头部。
后台定时从LRU old刷一部分页面,flush链表刷一部分页面。当后台线程刷脏页很慢时候,当需要加入一个页面到Buffer Pool中,并且没有剩余空闲缓冲页了,会释放LRU链表末尾的缓冲页,如果该页面有修改,则用户线程同步刷盘。 在系统特别忙,可能出现用户线程从flush链表刷脏页。这里涉及redo log的 checkpoint。
服务器启动时候可以申请多个Buffer Pool,5.7.5之后,Buffer Pool可以在服务器运行时候调整,这时候Buffer Pool不再是服务器一启动就向操作系统申请一大片连续区域,而是以chunk为基本单位申请,Buffer Pool是由若干个chunk为单位组成,chunk同样是一片连续的内存空间。