系统一级缓存设计
要设计一个高性能的系统,那么缓存肯定是一个绕不开的话题,合理使用缓存可以使得系统变得更“快”,响应时间也能大大减少。
那么如何设计一个缓存呢,这里将我系统现有的一个缓存功能记录下来,方便以后使用。这是一个简单的 K-V 的本地缓存,使用 Caffeine 作为具体的本地缓存框架,并且可以方便的更换底层框架而不用修改业务代码。
applicationContext.xml 中通过注入不同的 bean,从而让 CacheUtils 调用不同的缓存框架。
比如我这里使用的是 Caffeine 作为一级缓存,我也可以再建立一个 HashMapCache 类,继承 AbstractStringFirstLevelCache。再修改 applicationContext.xml 文件注入 HashMapCache 即可修改底层的缓存框架了。
public interface FirstLevelCache<K, V> {
V get(K key);
void set(K key, V value);
void delete(K key);
}
public abstract class AbstractFirstLevelCache<K, V> implements FirstLevelCache<K, V> {
}
public abstract class AbstractStringFirstLevelCache extends AbstractFirstLevelCache<String, String> {
abstract void set(String key, String value, Long expertTime, TimeUnit timeUnit);
}
public class CaffeineCache extends AbstractStringFirstLevelCache {
Log log = LogFactory.get();
LoadingCache<String, String> caffeineCache;
public CaffeineCache() {
// 查询二级缓存
LoadingCache<String, String> cache = Caffeine.newBuilder()
.recordStats()
.initialCapacity(100)
.maximumSize(1000)
.writer(new CacheWriter<String, String>() {
@Override
public void write(String key, String value) {
log.info("caffeineCache## write key=" + key + ", value=" + value);
}
@Override
public void delete(String key, String value, RemovalCause cause) {
log.info("caffeineCache## delete key=" + key);
}
})
.expireAfterWrite(30, TimeUnit.SECONDS) //一个元素将会在其创建或者最近一次被更新之后的一段时间后被认定为过期项
.build(key -> {
String value = RedisUtils.get(key);
if (StringUtils.isEmpty(value)) {
return null;
}
log.info("caffeineCache## get key from caffeineCache-redis, key: " + key);
return value;
});
caffeineCache = cache;
}
@Override
public String get(String key) {
String value = caffeineCache.get(key);
log.info("caffeineCache## get key from caffeineCache, key: " + key);
return value;
}
@Override
public void set(String key, String value) {
RedisUtils.set(key, value);
RedisUtils.expire(key, 300, TimeUnit.SECONDS);
log.info("caffeineCache## set key to redis, key: " + key);
}
@Override
void set(String key, String value, Long expertTime, TimeUnit timeUnit) {
RedisUtils.set(key, value);
RedisUtils.expire(key, expertTime, timeUnit);
log.info("caffeineCache## set key to redis, key: " + key + " expertTime: " + expertTime + " " + timeUnit.name());
}
@Override
public void delete(String key) {
caffeineCache.invalidate(key);
RedisUtils.delete(key);
log.info("caffeineCache## delete key from caffeineCache and redis, key: " + key);
}
}
@Component
public class CacheUtils {
static AbstractStringFirstLevelCache cache;
@Autowired
public void setCache(AbstractStringFirstLevelCache cache) {
CacheUtils.cache = cache;
}
/**
* 查找缓存,如果缓存不存在则从 redis 中获取,如果 redis 中也没有则返回 null
*
* @return value or null
*/
public static String get(String key) {
return cache.get(key);
}
/**
* 设置缓存,放入 redis 中,默认 5 min
*/
public static void set(String key, String value) {
cache.set(key, value);
}
/**
* 设置缓存,放入 redis 中,提供时间和时间单位
*/
public static void set(String key, String value, Long expertTime, TimeUnit timeUnit) {
cache.set(key, value, expertTime, timeUnit);
}
/**
* 删除缓存
*/
public static void delete(String key) {
cache.delete(key);
}
}
SpringMvc applicationContext.xml 注入 Bean
<bean id="cache" class="xxx.util.cache.CaffeineCache"/>