redis重启会清除数据吗_从零开始手写 redis(三)内存数据重启后如何不丢失?...
前言
我們在 從零手寫 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(三)内存数据重启后如何不丢失?...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql数据库实验3查询_MySQL数
- 下一篇: ssm jsp跳转jsp_去掉Shiro