缓存
当我们访问一条记录时,要把这个页的数据都加载到内存中并且缓存起来。在mysql启动时,就像操作系统申请了一篇连续的内存,缓冲池——buffer pool,默认128M。可以通过innodb_buffer_pool_size来修改。池中的缓存页默认也是16kb
每一个缓存页会有对应的控制块,占用一块内存(约为808字节,5%),存放了控制信息(表空间编号、页号、缓存页的地址、链表节点信息、锁和LSN信息)
free链表
用来管理哪些缓存页还没有被占用,也就是空闲的,会把每一个空闲缓存页对应的控制块加入到free链表中.每个节点占40字节,是单独申请的一块内存空间.每次加载了一个数据页后,就从free链表中取出一个空闲的缓存页,并把该缓存页对应的控制块的信息填上,然后从free链表中移除该控制块节点。
缓存页的哈希处理
查找buffer pool中的缓存页通过表空间号+页号作为key,缓存页是对应的value。来组成一个哈希表
flush链表
如果一个缓存页的数据被更新了,说明他是脏页了,会把这些脏页对应的控制块加入到flush链表中去
LRU链表
如果缓存页已经用满了,如何删除掉不用的缓存页呢。通过最近最少使用算法来淘汰(同redis),所以需要维护一个LUR链表,当刚加载到缓存池中时,会放到链表的头部。每一次访问就把对应的控制块移动到头部,这样尾部的节点就是最近最少使用的缓存页了。
问题:
预读:可能判断会读,但其实没读
-
全表扫描,会对缓冲池进行好几轮换血
解决:
- 把LRU链表按照比例分成冷热两块,默认冷链占37%,3/8左右。
- 这样预读就会放在old链的头部
- 全表扫描:首次加载的会被放到old区的头部。且在首次加载后的一定时间间隔内,再次访问改页的数据,不会把他放到young区(热链)默认1秒。
- 只有在热链区域的1/4后面,才会被移动到LRU链表的头部,就可以降低调整的频率。
刷新脏页到磁盘
后台有线程专门把脏页刷新到磁盘:
- 从
LRU链表
的冷数据中刷新一部分页面到磁盘。从尾部开始如果发现脏页就刷新到磁盘 - 从flush中刷新一部分到磁盘
多个buffer pool实例
如果多线程且并发高的情况下,单一的pool会因为多线程加锁而影响处理速度,如果pool比较大的时候(大于1g,如果小于1g设置多个是没用的),会拆分成若干个小pool,单独申请内存空间,这样多线程并发访问时可以提高并发能力。5.7.5之后可以动态调整pool的大小,这时,重新申请,再把旧的拷过来太耗时,所以会把连续申请改为chunk为单位申请,所以一个pool是有若干个chunk组成。包含了若干缓存页的chunk。如果pool size变化的时候就通过chunk增减来完成。不需要重新申请再复制