LRU算法核心就是淘汰最久未使用的数据,但是传统LRU算法有一些问题,即预读失效与缓冲池污染。 MYSQL采用冷热数据分离的方式对传统LRU算法进行了升级来管理innodb_buffer_pool。
1.什么是缓冲池污染
在批量读取数据或全表扫描的场景中或是访问冷数据时,大量的数据页被提前加载进来,但是并没有被高频访问,还导致了热点数据被后置到链表尾部甚至被冲出缓冲池。
1.1解决方案
冷热数据分离,形成新生代与老年代
- 核心思想:
当出现全表扫描时,按照传统LRU算法会直接给BP缓冲池从头到尾的全部换掉,一下子淘汰了已经稳定的热点数据。这样会导致BP的命中率直接被打乱,内存中无法查询,压力全部打到磁盘,最高频的SQL响应时间变得很慢。 - 具体实现:
innoDB升级的Buffer Pool LRU算法,用链表链接page页实现。LRU数据页链表,被5:3分为新生代young和老年代old,设计了冷热数据分离的处理方案。
针对在批量读取数据或全表扫描的场景中,既用到了Buffer Pool还对young区域热点数据没有影响,从而保证了Buffer Pool的查询命中率。新page页载入后先放入老年代的头部,挤掉老年代的尾部,如果没有被继续访问就会比新生代还早淘汰,如果被访问到就放入新生代的头部,把新生代的尾部的数据页放入老年代的头部,这个过程不会有数据页被清除 - Linux 操作系统对此的优化也是实现两个了 LRU 链表:活跃 LRU 链表(active_list)和非活跃 LRU 链表(inactive_list)
2.什么是预读失效
进行批量读取时冷数据冲入(新生代),导致大量热点数据被换出,缓冲池污染,命中率下降,产生大量的磁盘 I/O
2.1解决方案
提升加入新生代的门槛,防止冷数据进入
- 核心思想:
缓冲池污染的问题在于,加入新生代的门槛太低,当数据预读后被访问不应该直接加入新生代,而是设立一些门槛防止冷数据换出热点数据,比如MYSQ设置了时间判断,如果同一内存页访问的时间间隔过小,那么直接在老年代解决这个污染数据 - 具体实现:
批量读取的预读数据(冷数据)放入老年代,如果这个page在LRU链表中第二次访问间隔时间大于1000毫秒,则进入新生代。否则就留在old区。解决这些大量数据只会被访问一次,把热点数据淘汰的问题,它们只会待在老年代中,后续很快也会被淘汰。
这个间隔删除时间默认1000毫秒,由innodb_old_blocks_time参数决定。 - Linux 操作系统:是在内存页被访问第二次的时候,直接将页从 inactive list 升级到 active list 里。