系统性能提升利刃 | 缓存技术使用的实践与思考 (4)

2、新增数据:当有一个新的vr_phone创建时,存储到指针的上一个slot中。对于有slot冲突的场景,可以利用链表解决冲突,也可以利用数组解决冲突。链表和数组的考量标准还是依赖于单个slot的数据长度,如果数据过长,那么存储的数组会很长,则需要很大的内存空间才能满足,无法利用内存碎片的空间。

3、过期数据:指针每隔1秒移动一个slot,那么指针指向的slot就是需要过期的数据,因为新增的数据在环形slot转完一圈后,才会被指向到。

 

系统性能提升利刃 | 缓存技术使用的实践与思考

 


 

这样一种算法结构,将时间和空间巧妙地结合在了一起。新增元素的时间复杂度为O(1),直接插入待批量过期的slot的上一个位置即可;获取待删除元素列表时间复杂度也是O(1),就是待批量过期的slot位置。流行框架Netty、Kafka都有定时轮的影子。

当然,单层定时轮只适用于固定时间过期的场景,如果需要管理不同过期时间的元素,那么可以参考"多层定时轮算法",其实就是模拟现实世界的时针、分针、秒针的概念,建立多个单层定时轮,采用进位和退位的思想来管理元素的过期时间。

以上各种元素过期策略各有优缺点,可以根据业务的诉求来取舍。比如Memcache只是用了惰性删除,而Redis则同时使用了惰性删除和定期删除以结合二者的优点。

依赖空间的过期策略

此处只探讨最经典的三种策略FIFO、LRU、LFU的原理及实现方案,对于其它改进算法,感兴趣的同学可以自行查找。

a) FIFO:先进先出,当空间不足时,先进入的元素将会被移除。此方案并没有考虑元素的使用特性,可能最近频繁访问的一个元素会被移除,从而降低了缓存命中率。实现:基于LinkedHashMap的钩子函数实现FIFOMap。

// 链表头部是最近最少被访问的元素,需要被删除 public class FIFOMap<K, V> extends LinkedHashMap<K, V> { private int maxSize; //LinkedHashMap每次插入数据,默认都是链表tail;当accessOrder=false,元素被访问不会移动位置 public FIFOMap(int maxSize) { super(maxSize, 0.75f, false); this.maxSize = maxSize; } //每次put和putAll新增元素的时候都会触发判断;当下面函数=true时,就删除链表head元素 @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { return size() > maxSize; } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpzpdz.html