java 从零开始手写 redis(五)过期策略的另一种实现思路
2021-03-26 00:26
标签:hash exp service nts LEDE 存储 核心 pix 优秀 java从零手写实现redis(一)如何实现固定大小的缓存? java从零手写实现redis(三)redis expire 过期原理 java从零手写实现redis(三)内存数据如何重启不丢失? java从零手写实现redis(四)添加监听器 前面实现了 redis 的几个基本特性,其中在 expire 过期原理时,提到了另外一种实现方式。 这里将其记录下来,可以拓展一下自己的思路。 原来的实现方式见: java从零手写实现redis(三)redis expire 过期原理 https://mp.weixin.qq.com/s/BWfBc98oLqhAPLN2Hgkwow 以前的设计非常简单,符合最基本的思路,就是将过期的信息放在一个 map 中,然后去遍历清空。 为了避免单次操作时间过长,类似 redis,单次操作 100 个元素之后,直接返回。 不过定时任务之心时,其实存在两个不足: (1)keys 的选择不够随机,可能会导致每次循环 100 个结束时,真正需要过期的没有被遍历到。 不过 map 的随机比较蠢,就是将 map 的 keys 全部转为集合,然后通过 random 返回。 转的过程就是一个时间复杂度为 O(n) 的遍历,所以一开始没有去实现。 还有一种方式,就是用空间换区时间,存储的时候,同时存储在 list 中,然后随机返回处理,这个后续优化。 (2)keys 的遍历可能大部分都是无效的。 我们每次都是根据 keys 从前往后遍历,但是没有关心对应的过期时间,所以导致很多无效遍历。 本文主要提供一种以过期时间为维度的实现方式,仅供参考,因为这种方式也存在缺陷。 我们每次 put 放入过期元素时,根据过期时间对元素进行排序,相同的过期时间的 Keys 放在一起。 优点:定时遍历的时候,如果时间不到当前时间,就可以直接返回了,大大降低无效遍历。 缺点:考虑到惰性删除问题,还是需要存储以删除信息作为 key 的 map 关系,这样内存基本翻倍。 我们这里使用 每次存入新元素时,同时放入 sortMap 和 expireMap。 我们定义一个定时任务,100ms 执行一次。 实现源码如下: 这里直接遍历 sortMap,对应的 key 就是过期时间,然后和当前时间对比即可。 删除的时候,需要删除 expireMap/sortMap/cache。 惰性删除刷新时,就会用到 expireMap。 因为有时候刷新的 key 就一个,如果没有 expireMap 映射关系,可能要把 sortMap 全部遍历一遍才能找到对应的过期时间。 就是一个时间复杂度与空间复杂度衡量的问题。 实现过期的方法有很多种,目前我们提供的两种方案,都各有优缺点,我相信会有更加优秀的方式。 程序 = 数据结构 + 算法 redis 之所以性能这么优异,其实和其中的数据结构与算法用的合理是分不开的,优秀的框架值得反复学习和思考。 文中主要讲述了思路,实现部分因为篇幅限制,没有全部贴出来。 开源地址:https://github.com/houbb/cache 觉得本文对你有帮助的话,欢迎点赞评论收藏关注一波~ 你的鼓励,是我最大的动力~ java 从零开始手写 redis(五)过期策略的另一种实现思路 标签:hash exp service nts LEDE 存储 核心 pix 优秀 原文地址:https://blog.51cto.com/9250070/2539759以前的实现方式
核心思路
不足
基于时间的遍历
思路
基本属性定义
TreeMap
帮助我们进行过期时间的排序,这个集合后续有时间可以详细讲解了,我大概看了下 jdk1.8 的源码,主要是通过红黑树实现的。public class CacheExpireSort
放入元素时
@Override
public void expire(K key, long expireAt) {
List
定时任务的执行
定义
/**
* 线程执行类
* @since 0.0.3
*/
private static final ScheduledExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadScheduledExecutor();
public CacheExpireSort(ICache
执行任务
/**
* 定时执行任务
* @since 0.0.3
*/
private class ExpireThread implements Runnable {
@Override
public void run() {
//1.判断是否为空
if(MapUtil.isEmpty(sortMap)) {
return;
}
//2. 获取 key 进行处理
int count = 0;
for(Map.Entry
惰性删除刷新
@Override
public void refreshExpire(Collection
小结
文章标题:java 从零开始手写 redis(五)过期策略的另一种实现思路
文章链接:http://soscw.com/index.php/essay/67996.html