下面所写属于自己的理解,正确性不保证
缓存组件(memcached,redis等)一般都会设置缓存的内存容量(max memory),即所有键值对占内存的总和(不包括软件运行所需的内存)。当内存占用率达到 max memory 设置定的界限时(可能是80%),缓存组件则会执行用户设定的淘汰算法,淘汰旧数据,直到有足够的空闲内存,去存储新的键值对。
下面常用的淘汰算法:
1.LRU
最近最少使用(最近被访问过的,将来被访问的概率比较高)
实现原理:底层数据结构是链表。每次有KEY被访问时,将该KEY移至表头。淘汰时,从表尾开始淘汰,直到腾出足够的内存空间
命中率:当存在热点数据时,LRU的效率很好,但偶发性的、周期性的批量操作会导致LRU命中率急剧下降,缓存污染情况比较严重。
代价:命中时需要遍历链表,找到命中的数据块索引,然后需要将数据移到头部。
缺点:可能存在,某个KEY长期不被方法,或者很少被访问(即将被淘汰)。但在执行淘汰机制之前。这个KEY被访问了。导致这个访问频率低KEY没有被淘汰。
2.LRU-K
最近使用过K次 与LRU 基本相似,只是该算法认为,只要最近被访问过K次,将来被访问的概率才比较高
实现原理:底层数据结构是优先级队列,队列中的每个元素,有一个存储被访问的次数的引用计数。 当某个KEY被访问了K次之后,将该KEY移至表头。淘汰时,从表尾开始淘汰,直到腾出足够的内存空间。
与LRU对比:LRU-K的主要目的是为了解决LRU算法“缓存污染”的问题,其核心思想是将“最近使用过1次”的判断标准扩展为“最近使用过K次”。
命中率:LRU-K降低了“缓存污染”带来的问题,命中率比LRU要高。
代价:
a. 由于LRU-K还需要记录那些被访问过,因此内存消耗会比LRU要多;当数据量很大的时候,内存消耗会比较可观。
b. LRU-K需要基于时间进行排序(可以需要淘汰时再排序,也可以即时排序),CPU消耗比LRU要高。
3.LFU
根据数据的历史访问频率来淘汰数据,核心思想是历史访问中,被访问次数多的,将来被访问的概率该。淘汰历史访问次数低的(从低到高,直到空闲出足够的内存)
实现原理:底层数据结构是链表,链表中的每个元素都有一个引用计数。 每次访问,对应的应用计数+1。同时需要根据每个元素的引用计数,重新排序链表。
命中率:一般情况下,LFU效率要优于LRU,且能够避免周期性或者偶发性的操作导致缓存命中率下降的问题。但LFU需要记录数据的历史访问记录,一旦数据访问模式改变,LFU需要更长时间来适用新的访问模式,即:LFU存在历史数据影响将来数据的“缓存污染”效用。
复杂度:需要维护一个队列记录所有数据的访问记录,每个数据都需要维护引用计数。
代价:需要记录所有数据的访问记录,内存消耗较高;需要基于引用计数排序,性能消耗较高。
缺点:某些KEY,在某段时间访问发次数很高,但是过了某段时间之后,可能只需KEY都不会再访问。所以LFU在淘汰时,可能会淘汰这段时间访问不高,但后期访问频率高的数据
4.FIFO
先进先出 先来的先先滚,就是一个队列
实现原理:队列
命中率:靠运气的感觉
代价:低