前言

我们在 从零手写 cache 框架(一)实现固定大小的缓存 中已经初步实现了我们的 cache。

我们在 从零手写 cache 框架(一)实现过期特性 中实现了 key 的过期特性。

本节,让我们来一起学习一下如何实现类似 redis 中的 rdb 的持久化模式。

持久化的目的

我们存储的信息都是直接放在内存中的,如果断电或者应用重启,那么内容就全部丢失了。

有时候我们希望这些信息重启之后还在,就像 redis 重启一样。

load 加载

说明

在实现持久化之前,我们来看一下一个简单的需求:

如何在缓存启动的时候,指定初始化加载的信息。

实现思路

这个也不难,我们在 cache 初始化的时候,直接设置对应的信息即可。

api

为了便于后期拓展,定义 ICacheLoad 接口。

public interface ICacheLoad<K, V> {/*** 加载缓存信息* @param cache 缓存* @since 0.0.7*/void load(final ICache<K,V> cache);}

自定义初始化策略

我们在初始化的时候,放入 2 个固定的信息。

public class MyCacheLoad implements ICacheLoad<String,String> {@Overridepublic void load(ICache<String, String> cache) {cache.put("1", "1");cache.put("2", "2");}}

测试

只需要在缓存初始化的时候,指定对应的加载实现类即可。

ICache<String, String> cache = CacheBs.<String,String>newInstance().load(new MyCacheLoad()).build();Assert.assertEquals(2, cache.size());

持久化

说明

上面先介绍初始化加载,其实已经完成了 cache 持久化的一半。

我们要做的另一件事,就是将 cache 的内容持久化到文件或者数据库,便于初始化的时候加载。

接口定义

为了便于灵活替换,我们定义一个持久化的接口。

public interface ICachePersist<K, V> {/*** 持久化缓存信息* @param cache 缓存* @since 0.0.7*/void persist(final ICache<K, V> cache);}

简单实现

我们实现一个最简单的基于 json 的持久化,当然后期可以添加类似于 AOF 的持久化模式。

public class CachePersistDbJson<K,V> implements ICachePersist<K,V> {/*** 数据库路径* @since 0.0.8*/private final String dbPath;public CachePersistDbJson(String dbPath) {this.dbPath = dbPath;}/*** 持久化* key长度 key+value* 第一个空格,获取 key 的长度,然后截取* @param cache 缓存*/@Overridepublic void persist(ICache<K, V> cache) {Set<Map.Entry<K,V>> entrySet = cache.entrySet();// 创建文件FileUtil.createFile(dbPath);// 清空文件FileUtil.truncate(dbPath);for(Map.Entry<K,V> entry : entrySet) {K key = entry.getKey();Long expireTime = cache.expire().expireTime(key);PersistEntry<K,V> persistEntry = new PersistEntry<>();persistEntry.setKey(key);persistEntry.setValue(entry.getValue());persistEntry.setExpire(expireTime);String line = JSON.toJSONString(persistEntry);FileUtil.write(dbPath, line, StandardOpenOption.APPEND);}}}

定时执行

上面定义好了一种持久化的策略,但是没有提供对应的触发方式。

我们就采用对用户透明的设计方式:定时执行。

public class InnerCachePersist<K,V> {private static final Log log = LogFactory.getLog(InnerCachePersist.class);/*** 缓存信息* @since 0.0.8*/private final ICache<K,V> cache;/*** 缓存持久化策略* @since 0.0.8*/private final ICachePersist<K,V> persist;/*** 线程执行类* @since 0.0.3*/private static final ScheduledExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadScheduledExecutor();public InnerCachePersist(ICache<K, V> cache, ICachePersist<K, V> persist) {this.cache = cache;this.persist = persist;// 初始化this.init();}/*** 初始化* @since 0.0.8*/private void init() {EXECUTOR_SERVICE.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {try {log.info("开始持久化缓存信息");persist.persist(cache);log.info("完成持久化缓存信息");} catch (Exception exception) {log.error("文件持久化异常", exception);}}}, 0, 10, TimeUnit.MINUTES);}}

定时执行的时间间隔为 10min。

测试

我们只需要在创建 cache 时,指定我们的持久化策略即可。

ICache<String, String> cache = CacheBs.<String,String>newInstance().load(new MyCacheLoad()).persist(CachePersists.<String, String>dbJson("1.rdb")).build();
Assert.assertEquals(2, cache.size());
TimeUnit.SECONDS.sleep(5);

为了确保文件持久化完成,我们沉睡了一会儿。

文件效果

  • 1.rdb

生成的文件内容如下:

{"key":"2","value":"2"}
{"key":"1","value":"1"}

对应的缓存加载

我们只需要实现以下对应的加载即可,解析文件,然后初始化 cache。

/*** 加载策略-文件路径* @author binbin.hou* @since 0.0.8*/
public class CacheLoadDbJson<K,V> implements ICacheLoad<K,V> {private static final Log log = LogFactory.getLog(CacheLoadDbJson.class);/*** 文件路径* @since 0.0.8*/private final String dbPath;public CacheLoadDbJson(String dbPath) {this.dbPath = dbPath;}@Overridepublic void load(ICache<K, V> cache) {List<String> lines = FileUtil.readAllLines(dbPath);log.info("[load] 开始处理 path: {}", dbPath);if(CollectionUtil.isEmpty(lines)) {log.info("[load] path: {} 文件内容为空,直接返回", dbPath);return;}for(String line : lines) {if(StringUtil.isEmpty(line)) {continue;}// 执行// 简单的类型还行,复杂的这种反序列化会失败PersistEntry<K,V> entry = JSON.parseObject(line, PersistEntry.class);K key = entry.getKey();V value = entry.getValue();Long expire = entry.getExpire();cache.put(key, value);if(ObjectUtil.isNotNull(expire)) {cache.expireAt(key, expire);}}//nothing...}
}

然后在初始化时使用即可。

小结

到这里,我们一个类似于 redis rdb 的持久化就简单模拟完成了。

但是对于 rdb 这里还有需要可优化点,比如 rdb 文件的压缩、格式的定义、CRC 校验等等。

redis 考虑到性能问题,还有 AOF 的持久化模式,二者相辅相成,才能达到企业级别的缓存效果。

我们后续将陆续引入这些特性。

对你有帮助的话,欢迎点赞评论收藏关注一波~

你的鼓励,是我最大的动力~

redis重启会清除数据吗_从零开始手写 redis(三)内存数据重启后如何不丢失?...相关推荐

  1. node怎么把token放到redis_从零开始手写 redis(八)朴素 LRU 淘汰算法性能优化

    前言 java从零手写实现redis(一)如何实现固定大小的缓存? java从零手写实现redis(三)redis expire 过期原理 java从零手写实现redis(三)内存数据如何重启不丢失? ...

  2. hashmap remove 没释放内存_java从零开始手写 redis(13)HashMap 源码原理详解

    为什么学习 HashMap 源码? 作为一名 java 开发,基本上最常用的数据结构就是 HashMap 和 List,jdk 的 HashMap 设计还是非常值得深入学习的. 无论是在面试还是工作中 ...

  3. druid连接池初始化慢_从零开始手写 mybatis (三)jdbc pool 从零实现数据库连接池

    前景回顾 第一节 从零开始手写 mybatis(一)MVP 版本 中我们实现了一个最基本的可以运行的 mybatis. 第二节 从零开始手写 mybatis(二)mybatis interceptor ...

  4. mybatis 无法初始化类_从零开始手写 mybatis(一)MVP 版本

    什么是 MyBatis ? MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过程以及高级映射. MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集. MyB ...

  5. redis setnx 分布式锁_手写Redis分布式锁

    分布式锁使用场景 现在的系统都是集群部署,每个服务都不是单节点的了.比如库存服务,可能部署到3台机器上分别命名为节点1,节点2,节点3.库存服务需要扣减库存,扣减库存肯定需要锁吧,如果使用Lock或者 ...

  6. 深蓝学院《从零开始手写VIO》作业七

    深蓝学院<从零开始手写VIO>作业七 深蓝学院<从零开始手写VIO>作业七 深蓝学院<从零开始手写VIO>作业七 将第二讲中的仿真数据(视觉特征,imu数据)接入V ...

  7. 深蓝学院《从零开始手写VIO》作业六

    深蓝学院<从零开始手写VIO>作业五 深蓝学院<从零开始手写VIO>作业六 1. 证明题 2. 代码题 深蓝学院<从零开始手写VIO>作业六 1. 证明题 证明Dy ...

  8. 深蓝学院《从零开始手写VIO》作业五

    深蓝学院<从零开始手写VIO>作业五 1. 完成Bundle Adjustment求解器 2. 完成测试函数 3. 论文总结 1. 完成Bundle Adjustment求解器 完成单目 ...

  9. 深蓝学院《从零开始手写VIO》作业三

    深蓝学院<从零开始手写VIO>作业三 深蓝学院<从零开始手写VIO>作业三 1. 代码修改 2. 公式推导 3. 公式证明: 深蓝学院<从零开始手写VIO>作业三 ...

最新文章

  1. 介绍一本零基础入门Python数据分析的书
  2. SQL中LIKE的妙用
  3. 菜鸟学习JavaScript小实验之函数引用
  4. 计算机应用与网络讲义,计算机基础讲义
  5. linux下的五种io模型,Linux下的五种IO模型
  6. ajax提交手机号去数据库验证并返回状态值
  7. springboot不会运行gc_SpringBoot 和JVM 调优(深度好文,建议收藏)
  8. asterisk 支持 VP8 video编码 实现安卓的视频通话
  9. Flink读取Kafka报错:KafkaException ByteArrayDeserializer is not an instance Deserializer
  10. reduce 阶段遍历对象添加到ArrayList中的问题
  11. 深入理解和使用nginx
  12. Filter动态代理敏感词汇过滤
  13. ROS突然所有的工作空间都无法启动launch,但roscore可以用
  14. 【微信小程序开发日记01】和风天气OUC之初步构想
  15. 【Java算法学习】鸡兔同笼问题
  16. Reggie外卖项目 —— 开发环境搭建
  17. 2020商业风口复盘:巨变下的重构与新生
  18. 用c语言实现100-1000之内的水仙花数
  19. word转图片,pdf转图片,doc转图片,docx转图片
  20. c#获取计算机制造商信息

热门文章

  1. hdu3449 有依赖的背包问题
  2. hdu2413 二分+二分匹配
  3. 【Windows 逆向】OD 调试器工具 ( OD 调试数据时硬件断点对应的关键代码 | 删除硬件端点恢复运行 )
  4. 【Android 逆向】x86 CPU 架构体系 ( 堆内存 | 栈内存 | 函数调用 )
  5. 【计算机网络】网络层 : IP 数据报分片 ( 数据分片机制 | 分片示例 | 三种数据长度单位 )
  6. 【Android FFMPEG 开发】FFMPEG 获取编解码器 ( 获取编解码参数 | 查找编解码器 | 获取编解码器上下文 | 设置上下文参数 | 打开编解码器 )
  7. 【Android 应用开发】 Ubuntu 安装 Android Studio (旧版本|仅作参考)
  8. 【面向对象设计模式】 接口型模式 (一)
  9. 剑指Offer——合并两个排序的链表
  10. VS2013试用期结束后如何激活