目录
- Buffer Pool回顾
- Buffer Pool内部组成
- freelist
- flushlist
- LRU链表管理以及改进
Buffer Pool回顾
我们知道针对数据库的增删改删操作都是在Buffer Pool中完成的,一条sql的执行步骤可以认为是这样的:
1、innodb存储引擎首先在缓冲池中查询有没有对应的数据,有就直接返回
2、如果不存在,则去磁盘进行加载,并加入缓冲池
3、同时该记录会被加上独占锁,防止多人修改,出现数据不一致
而且我们知道,可以通过设置my.cnf
配置中的innodb_buffer_pool_size
来修改缓冲池大小,加快sql查询速度,当然也需要注意设置过大会造成系统swap空间被占用,导致系统变慢降低查询性能。
Buffer Pool内部组成
缓冲池对应一片连续内存,我们将其划分为大小为16kb的页(与innodb对应),这些页称为缓冲页。
为了很好的管理这些页,设计者为每个缓冲页都创建了一些控制信息:表空间编号、页号、缓冲页在缓冲池中的地址、链表节点信息等。将每个页对应的控制信息占用的一块内存称为一个控制块。控制块与缓冲页一一对应,都存放在缓冲池中。
在Mysql启动时,会自己完成对缓冲池的初始化:向操作系统申请内存,自己划分成若干对控制块和缓冲页。
freelist
当我们从磁盘中load一个数据页到缓冲池中,我们应该放到哪个缓冲页中呢?
很显然我们应该把数据页放到“空闲”的缓冲页中。
设计者将所有空闲的缓冲页对应的控制块作为一个节点放到一个链表中,称为freelist。每次从freelist中取出一个空闲的缓冲页中,并且将该缓冲页对应的控制块信息填上,然后将该节点移除,表示缓冲页已经被使用了
flushlist
当一个控制块节点被从freelist中移除,说明该页已经被使用了。如果这种“使用操作”是对数据进行修改的话,那么必定需要将该页数据flush到磁盘上。但是每次修改一页就将那一页flush的话,磁盘IO占用率高。所以每次修改缓冲页后,将这些脏页控制块放入一个fulshlist上。当flush时机到了,就把flushlist节点对应的缓冲页刷新搭配磁盘上。
LRU链表管理以及改进
缓冲池内存有限,当freelist中没有多余的空闲缓冲页,就需要把某些旧的缓冲页从缓冲池中移除,然后把新的数据页放进来。为了提高内存命中率,使用LRU。
但是普通的LRU不能解决下面的问题;
1、加载到缓冲池的页不一定被用到(针对于预读)
2、如果有非常多的使用频率低的页被同时加载到缓冲池中,则可能会把那些使用频率非常高的页从缓冲池中淘汰。(针对全表扫描)
关于innodb对于LRU的改进见如链接:
MySQL——Innodb改进LRU算法
当然还有进一步的优化:
对于young区域的缓冲页,每次访问一个缓冲页就要把它移动到LRU链表的头部,开销比较大。毕竟,young区域的缓冲页都是热点数据。所以我们可以这样优化:只有被访问的缓冲页位于young区域1/4的后面时,才会被移动到LRU链表头部。也就是说我们将young的前0.25部分称为very young,very young里面的数据访问不会移动到头部,因为大家访问频率都是非常高的。
提醒一下,在LRUlist的节点不是freelist节点,可能是flushlist节点。不理解的话,再去上面看看两个list定义。
然而这一切的目的只有一个:尽量高效地提高缓冲池命中率。