当前位置: 首页 > 工具软件 > 豆豆Pool > 使用案例 >

buffer pool终于略知一二了

姜景焕
2023-12-01

buffer pool终于略知一二了

mysql一文之buffer pool

最近看到一个有意思的东西,就是关于mysql的数据是怎么被高效读取的。

innodb的缓冲池是什么,为什么要去使用它?

mysql为什么会引入buffer pool这个机制呢?因为读取数据的时候,如果每次都去访问磁盘并把数据加载到内存,这样都会经过磁盘io。io操作会很费时间的,效率会很低,所以加入它查询时候会走缓存,效率会有很大提升。
innodb的缓冲池里面都存放的什么呢?存放的就是把磁盘上的数据都加载到缓冲池的东西,即表数据和索引数据。
这里我们要明白一个概念就是磁盘预读:磁盘读写是以页为单位的(一般是4K,包括8个扇区,一个扇区是125B),不是按需读写,这个其实就是用了局部性的原因,当你使用一块数据时候,临近的数据下次很可能会被使用到。
当然你可能会问,既然缓存这么快,为什么不把磁盘上的数据都加载到内存到缓冲区呢?因为这个内存是有限的,就像你的电脑,硬盘可能会很大,但是内存只有16G。

innodb如何来管理这些缓存页

这个估计首相想到的就是最经典的LRU(least recently used),这个算法想必不用做太多的介绍,传统的可以把它想象一个hashMap和DLL(double linked list)结合实现LRU,hashMap来快速查找节点的位置,链表进行移动。
举个例子:比如LRU的长度是5,已经缓存了5个页了,再缓存的时候,就需要将tail舍掉,新页加入到head。
其实mysql是没有用LRU的,为什么不用呢?是因为会有两个问题,一个就是预读失效,一个是缓冲池污染。
预读失效:这个可以理解成,我把数据页都缓存到了缓冲池里,但是mysql一直没有从页中读取数据。
优化的话其实有两个思路:
a:让预读失效的页在LRU里停留时间尽可能短一点
b:让真正被读取到的页才加入到head
具体方法就是将lru分成两部分,一个是新生代(new sublist)和老生代(old sublist),新生代的tail指向老生代的head,新页加入缓冲池只加入到老生代头部,如果数据真正被读取,才加入到新生代的头部;
如果数据没有被读取,则更早的被淘汰。
这里说几个参数,刚看了下就是innodb_old_blocks_pct参数:代表老生代占整个LRU链长度的比例,默认是37,即新生代与老生代长度比例是63:37。
innodb_old_blocks_time:老生代时间窗口,默认是1000,即1s。
缓冲池污染:比如批量查询大量数据,可能会导致缓冲池的所有页被替换出去,导致大量的热数据(频繁去读取的)被换出,mysql性能下降(比如根据姓名去模糊查询数据,这里主要说的是全模糊,会全表扫描,这种数据只会被访问一次就加到了新生代的头部)。
说到这里发现了没,就是比如根据%涛%去查询,会把页都加入到老生代的头部,从页里读取行加入到新生代的头部,筛选符合条件的数据加入到结果集,直到把全部的row都访问完。
解决的话就引入了一个老生代停留时间窗口,假如是T,插入老生代的页即使被访问,也不会被立刻加到新生代的头部,只有满足被访问并且在新生代停留时间大于T,才会放进新生代的头部。
说白了buffer pool可以降低随机访问磁盘,比较实用。

最后我想说奋斗的人生是没有任何意义的,我们共同加油!

 类似资料: