目前还没着笔,初略一想MySQL和操作系统应该都是使用的年轻代和老生代的改进策略,而Redis使用的是随机抽的策略。MySQL
下面的LRU限制在缓存的数据页当中(控制块等应该也是不会淘汰的)有了缓存池之后
由三个链表的功能可以知道,最开始free list是满的(数据还没有读入),随后会越来越少。Buffer Pool 的大小是有限的,对于一些频繁访问的数据我们希望可以一直留在 Buffer Pool 中,而一些很少访问的数据希望可以在某些时机可以淘汰掉,从而保证 Buffer Pool 不会因为满了而导致无法再缓存新的数据,同时还能保证常用数据留在 Buffer Pool 中。
MySQL解决这两个问题的基本思路:具体来说,为了解决缓存污染:MySQL 是这样做的,进入到 young 区域条件增加了一个停留在 **old** 区域的时间判断。
- 解决预读失效:将缓存区分为新生代和老生代。预读的数据读入老生代(优先淘汰),如果真正使用了才会加入新生代。
- 解决缓存污染:由于预读的数据只要使用了就加入新生代,即使用一次就加入新生代,后面就算不用了新生代也全部被这些只用了一次的数据污染了。因此解决方案就是提高进入新生代的代价。
这个优化非常有意思,因为越靠前的区域就是越热点的数据,可能来回访问就是那几个热点数据,因此特别热点的数据访问就不用来回移动了hhh。操作系统
参考:当 CPU 访问的页面不在物理内存时,便会产生一个缺页中断,请求操作系统将所缺页调入到物理内存。
不同的是操作系统淘汰的是页表,而MySQL淘汰的是数据页。其实本质上都是一样的hhh。
所缺页调入到物理内存的步骤大概为:
- 首先查找页表,找对应的页表项,如果页表项中的状态位为「无效的」,就触发缺页中断。
- 去磁盘找对应页面在磁盘中的位置,读取出来。
- 准备换入:物理内存中找空闲页,有就换入。没有就自然涉及了内存页面置换的一些策略了。
- 将页表项中的状态位设置为[有效的],重新执行对应的触发缺页中断的代码。
MySQL用的是LRU,效果好,但是代价也是比较大的。
感觉是相对于FIFO算法,每个页面多了一次的选择机会。
要增加一个计数器来实现,这个硬件成本是比较高的,另外如果要对这个计数器查找哪个页面访问次数最小,查找链表本身,如果链表长度很大,是非常耗时的,效率不高。Redis
但还有个问题,LFU 算法只考虑了频率问题,没考虑时间的问题,比如有些页面在过去时间里访问的频率很高,但是现在已经没有访问了,而当前频繁访问的页面由于没有这些页面访问的次数高,在发生缺页中断时,就会可能会误伤当前刚开始频繁访问,但访问次数还不高的页面。
那这个问题的解决的办法还是有的,可以定期减少访问的次数,比如当发生时间中断时,把过去时间访问的页面的访问次数除以 2,也就说,随着时间的流失,以前的高访问次数的页面会慢慢减少,相当于加大了被置换的概率。
参考:Redis 内存淘汰策略共有八种,这八种策略大体分为「不进行数据淘汰」和「进行数据淘汰」两类策略。
MySQL中考虑的是LRU算法带来的预读失效和Buffer Pool污染的问题(简单LRU算法效果不好),而Redis是从LRU算法维护成本来考虑的。
这样实现就自然没有了链表的开销和移动链表节点的开销了。但是多了一个额外字段(记录最后一次访问时间)的开销。Redis 是如何实现 LFU 算法的?
可以在上文中看到操作系统实现LFU算法的问题有:1.加一个访问计数器有硬件成本 2.虽叫LFU,但是只考虑了次数,而没有考虑时间(频率)。在LFU算法中,Redis实现相比于操作系统实现更好,真正的考虑了访问频率这个问题。
本文由博客一文多发平台 OpenWrite 发布!
欢迎光临 IT评测·应用市场-qidao123.com (https://dis.qidao123.com/) | Powered by Discuz! X3.4 |