ride
public long read() {
return 0;
}
})
.build(new CacheLoader<String, Optional<T>>() {
@Override
public Optional<T> load(String key) {
T cacheObject = getObjectFromDb(key);
log.debug("find the local guava cache of key: {} is {}", key, cacheObject);
return Optional.ofNullable(cacheObject);
}
});
public T get(String key) {
try {
if (StrUtil.isEmpty(key)) {
return null;
}
Optional<T> optional = guavaCache.get(key);
return optional.orElse(null);
} catch (ExecutionException e) {
log.error("get cache object from guava cache failed.");
e.printStackTrace();
return null;
}
}
public void invalidate(String key) {
if (StrUtil.isEmpty(key)) {
return;
}
guavaCache.invalidate(key);
}
public void invalidateAll() {
guavaCache.invalidateAll();
}
/**
* 从数据库加载数据
* @param id
* @return
*/
public abstract T getObjectFromDb(Object id);
}
我们将getObjectFromDb方法留给子类自己去实现。以下是例子:
/**
* @author valarchie
*/
@Component
@Slf4j
@RequiredArgsConstructor
public class GuavaCacheService {
@NonNull
private ISysDeptService deptService;
public final AbstractGuavaCacheTemplate<SysDeptEntity> deptCache = new AbstractGuavaCacheTemplate<SysDeptEntity>() {
@Override
public SysDeptEntity getObjectFromDb(Object id) {
return deptService.getById(id.toString());
}
};
}
Redis(三级缓存)
项目中会持续增长的数据比如用户、订单等相关数据。这些数据比较多,不适合放在内存级缓存当中,而应放在缓存中间件Redis当中去。Redis是支持持久化的,当我们的服务器重新启动时,依然可以从Redis中加载我们原先存储好的数据。
但是使用Redis缓存还有一个可以优化的点。我们可以自己本地再做一个局部的缓存来缓存Redis中的数据来减少网络IO请求,提高数据访问速度。 比如我们Redis缓存中有一万个用户的数据,但是一分钟之内可能只有不到1000个用户在请求数据。我们便可以在Redis中嵌入一个局部的Guava缓存来提供性能。以下是RedisCacheTemplate.
/**
* 缓存接口实现类 三级缓存
* @author valarchie
*/
@Slf4j
public class RedisCacheTemplate<T> {
private final RedisUtil redisUtil;
private final CacheKeyEnum redisRedisEnum;
private final LoadingCache<String, Optional<T>> guavaCache;
public RedisCacheTemplate(RedisUtil redisUtil, CacheKeyEnum redisRedisEnum) {
this.redisUtil = redisUtil;
this.redisRedisEnum = redisRedisEnum;
this.guavaCache = CacheBuilder.newBuilder()
// 基于容量回收。缓存的最大数量。超过就取MAXIMUM_CAPACITY = 1 << 30。依靠LRU队列recencyQueue来进行容量淘汰
.maximumSize(1024)
.softValues()
// 没写访问下,超过5秒会失效(非自动失效,需有任意put get方法才会扫描过期失效数据。
// 但区别是会开一个异步线程进行刷新,刷新过程中访问返回旧数据)
.expireAfterWrite(redisRedisEnum.expiration(), TimeUnit.MINUTES)
// 并行等级。决定segment数量的参数,concurrencyLevel与maxWeight共同决定
.concurrencyLevel(64)
// 所有segment的初始总容量大小
.initialCapacity(128)
.build(new CacheLoader<String, Optional<T>>() {
@Override
public Optional<T> load(String cachedKey) {
T cacheObject = redisUtil.getCacheObject(cachedKey);
log.debug("find the redis cache of key: {} is {}", cachedKey, cacheObject);
return Optional.ofNullable(cacheObject);
}
});
}
/**
* 从缓存中获取对象 如果获取不到的话 从DB层面获取
* @param id
* @return
*/
public T getObjectById(Object id) {
String cachedKey = generateKey(id);
try {
Optional<T> optional = guavaCache.get(cachedKey);
// log.debug("find the guava cache of key: {}", cachedKey);
if (!optional.isPresent()) {
T objectFromDb = getObjectFromDb(id);
set(id, objectFromDb);
return objectFromDb;
}
return optional.get();
} catch (ExecutionException e) {
e.printStackTrace();
re