Java实现本地缓存
java实现本地缓存缓存缓存,将程序或系统中重复用到的数据缓存在内存中,加快访问速度,减少系统开销,提高系统效率的方案。数据存储方式主要分2种:文件 数据保存在文件中,做持久化。内存 数据保存在内存中,也就是创建一个静态内存区域,本文中数据保存在静态区域,使用MAP保存key\value数据。开源缓存框架RedisRedis是基于内存、可持久化的日志型、Key-Value数据库高性能存储系统,并提
java实现本地缓存
缓存
缓存,将程序或系统中重复用到的数据缓存在内存中,加快访问速度,减少系统开销,提高系统效率的方案。
数据存储方式主要分2种:
- 文件 数据保存在文件中,做持久化。
- 内存 数据保存在内存中,也就是创建一个静态内存区域,本文中数据保存在静态区域,使用MAP保存key\value数据。
开源缓存框架
- Redis
Redis是基于内存、可持久化的日志型、Key-Value数据库高性能存储系统,并提供多种语言的API. - memcached
是一个自由开源的,高性能,分布式内存对象缓存系统。基于内存的key-value存储,用来存储小块的任意数据(字符串、对象)。
解决场景
目前在开发的项目中,不希望引入太多外部服务,引入分布式缓存框架例如Redis,还需要部署与维护,这会大大增加对项目的维护成本。
Java实现本地缓存框架逻辑
框架基本定义
- 数据存储格式 key-value,仅针对字符串缓存。
- 数据有效性,增加超时时间失效逻辑
- 失效策略:定期删除与懒惰淘汰并行
定期删除策略
定期删除策略是每隔一段时间检测已过期的缓存,并且降之删除。这个策略的优点是能够确保过期的缓存都会被删除。同时也存在着缺点,过期的缓存不一定能够及时的被删除,这跟我们设置的定时频率有关系,另一个缺点是如果缓存数据较多时,每次检测也会给 cup 带来不小的压力。
懒惰淘汰策略
懒惰淘汰策略是在使用缓存时,先判断缓存是否过期,如果过期将它删除,并且返回空。这个策略的优点是只有在查找的时候,才判断是否过期,对 CUP 影响较小。同时这种策略有致命的缺点,当存入了大量的缓存,这些缓存都没有被使用并且已过期,都将成为无效缓存,这些无效的缓存将占用你大量的内存空间,最后导致服务器内存溢出。
我们简单的了解了一下 Redis 的两种过期缓存处理策略,每种策略都存在自己的优缺点。所以我们在使用过程中,可以将两种策略组合起来,结合效果还是非常理想的。
逻辑顺序
- 定义缓存map, 定义定时移除时效数据的任务,在static代码块中初始化。
- 定义缓存对象,包含值和过期时间。
- 定义定时移除时效数据的异步任务。
- 实现俩种时效策略移除数据的逻辑 remove + removeAll.
- 实现put与get方法。
框架中用到的定时任务模块
private final static ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
executor.scheduleAtFixedRate(new Task(), INITIAL_DELAY_TIME, PERIOD_TIME, TimeUnit.SECONDS);
初始化线程池,启动定时执行任务scheduleAtFixedRate
- new Task() 定时任务执行的具体业务逻辑
- INITIAL_DELAY_TIME 启动后延迟时间执行(秒)
- PERIOD_TIME 每次执行间隔(秒)
执行结果
定时任务策略:延迟5秒执行,每隔5秒执行一次 (实际失效策略时间可以放长一点)
首先添加缓存数据
LocalCache.put("a", "10a", 7);
LocalCache.put("b", "10b", 12);
LocalCache.put("c", "10c", 30);
2019-12-25 21:00:19,914 [http-nio-9060-exec-1] INFO LocalCache 62 - 添加缓存,key=a, value=10a, expire=7秒
2019-12-25 21:00:19,915 [http-nio-9060-exec-1] INFO LocalCache 62 - 添加缓存,key=b, value=10b, expire=12秒
2019-12-25 21:00:19,915 [http-nio-9060-exec-1] INFO LocalCache 62 - 添加缓存,key=c, value=10c, expire=30秒
定时任务执行第二次 10s后,移除缓存数据a
2019-12-25 21:00:24,919 [pool-3-thread-1] INFO LocalCache 89 - 定期删除策略: 开始执行, store={a=LocalCache.Cache(value=10a, expire=1577278826915), b=LocalCache.Cache(value=10b, expire=1577278831915), c=LocalCache.Cache(value=10c, expire=1577278849915)}
2019-12-25 21:00:29,918 [pool-3-thread-1] INFO LocalCache 89 - 定期删除策略: 开始执行, store={a=LocalCache.Cache(value=10a, expire=1577278826915), b=LocalCache.Cache(value=10b, expire=1577278831915), c=LocalCache.Cache(value=10c, expire=1577278849915)}
2019-12-25 21:00:29,919 [pool-3-thread-1] INFO LocalCache 94 - 定期删除策略: 移除超时失效数据, key=a, value=10a, time=1577278826915
定时任务执行第三次 15s后,移除缓存数据b
2019-12-25 21:00:34,918 [pool-3-thread-1] INFO LocalCache 89 - 定期删除策略: 开始执行, store={b=LocalCache.Cache(value=10b, expire=1577278831915), c=LocalCache.Cache(value=10c, expire=1577278849915)}
2019-12-25 21:00:34,919 [pool-3-thread-1] INFO LocalCache 94 - 定期删除策略: 移除超时失效数据, key=b, value=10b, time=1577278831915
定时任务执行第六次 30s后,移除缓存数据c
2019-12-25 21:00:39,918 [pool-3-thread-1] INFO LocalCache 89 - 定期删除策略: 开始执行, store={c=LocalCache.Cache(value=10c, expire=1577278849915)}
2019-12-25 21:00:44,919 [pool-3-thread-1] INFO LocalCache 89 - 定期删除策略: 开始执行, store={c=LocalCache.Cache(value=10c, expire=1577278849915)}
2019-12-25 21:00:49,918 [pool-3-thread-1] INFO LocalCache 89 - 定期删除策略: 开始执行, store={c=LocalCache.Cache(value=10c, expire=1577278849915)}
2019-12-25 21:00:49,919 [pool-3-thread-1] INFO LocalCache 94 - 定期删除策略: 移除超时失效数据, key=c, value=10c, time=1577278849915
2019-12-25 21:00:54,918 [pool-3-thread-1] INFO LocalCache 89 - 定期删除策略: 开始执行, store={}
源码
package com.core.mall.config.cache;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.*;
/**
* 本地缓存服务
* key = 字符串 | value = 字符串 | expire = 秒(有效时间)
*
* put 添加缓存
* get 读取缓存值
*
* 失效策略:
* 1. 定期删除策略:启动1个线程,每2分钟扫描一次,超时数据移除
* 2. 懒惰淘汰策略:每次访问时校验有效性,如果失效移除
*/
public class LocalCache {
private final static Logger logger = LoggerFactory.getLogger(LocalCache.class);
/**
* 启动开始后延迟5秒执行时效策略
*/
private static final int INITIAL_DELAY_TIME = 5;
/**
* 执行时效策略间隔时间
*/
private static final int PERIOD_TIME = 5;
/**
* 本地缓存map
*/
private static ConcurrentHashMap store;
/**
* 执行时效策略线程池
*/
private final static ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
/*
* 静态代码块
*
* 初始化缓存map
* 添加时效策略定时线程任务
*/
static {
store = new ConcurrentHashMap<>();
executor.scheduleAtFixedRate(new Task(), INITIAL_DELAY_TIME, PERIOD_TIME, TimeUnit.SECONDS);
}
public static void put(String key, String value) {
put(key, value, 0);
}
/**
* 设置缓存
* @param key 唯一key
* @param value 值
* @param expire 超时时间-单位(s/秒)
*/
public static void put(String key, String value, long expire) {
logger.info("添加缓存,key={}, value={}, expire={}秒", key, value, expire);
if (expire > 0) {
store.put(key, new Cache(value, expire));
} else {
store.put(key, new Cache(value));
}
}
public static String get(String key) {
Cache cache = store.get(key);
if (cache == null) {
return null;
}
if (cache.getExpire() > 0 && cache.getExpire() < System.currentTimeMillis()) {
remove(key);
return null;
}
return store.get(key).getValue();
}
private static void remove(String key) {
Cache cache = store.remove(key);
logger.info("懒惰淘汰策略: 移除超时失效数据, cache={}", cache);
}
private static void removeAll() {
logger.info("定期删除策略: 开始执行, store={}", store);
for (String key : store.keySet()) {
Cache cache = store.get(key);
if (cache.getExpire() > 0 && cache.getExpire() < System.currentTimeMillis()) {
store.remove(key);
logger.info("定期删除策略: 移除超时失效数据, key={}, value={}, time={}", key, cache.getValue(), cache.getExpire());
}
}
}
/**
* 定时移除时效数据任务
*/
private static class Task implements Runnable {
@Override
public void run() {
try {
LocalCache.removeAll();
} catch (Exception e) {
logger.info("定期删除策略异常", e);
}
}
}
/**
* 本地缓存对象
*/
@Data
private static class Cache {
private String value;
private long expire = 0;
Cache(String value, long expire) {
this.value = value;
this.expire = System.currentTimeMillis() + expire * 1000;
}
Cache(String value) {
this.value = value;
}
}
}
更多推荐
所有评论(0)