为什么要用缓存
- 提高应用性能。获取某个数据,原来可能非常耗资源(CPU资源,IO资源,时间等),用了缓存之后,可以非常快速的获取数据
- 提高并发。节约了资源,同等资源情况下,服务可承载更多的请求。
- 提高服务可靠性。服务的性能,并发都提高了。在同等业务场景下,服务将更稳定。
缓存问题
问题 | 描述 | 解决方案 |
---|
穿透 | 请求不存在于缓存和后端存储中的数据,使得所有请求都落到后端存储上,导致系统瘫痪。 | 布隆过滤器, 缓存空值, 事件驱动缓存预热 |
击穿 | 高并发访问下,某个热点数据失效后,大量请求同时涌入后端存储,导致后端存储负载增大、响应时间变慢,甚至瘫痪 | 锁,续期,事件驱动缓存预热 |
雪崩 | 缓存中大量的数据同时失效或过期,后续请求都落到后端存储上,从而引起系统负载暴增、性能下降甚至瘫痪 | 过期随机,事件驱动更新 |
从以上问题看,事件驱动更新似乎可以解决所有问题,但只针对数据量较小,数据全量缓存的场景,才适合事件驱动更新策略
缓存方案
缓存方式 | 数据量 | 数据使用频率 | 数据修改频率 |
---|
堆 | 小 | 使用超量大 | 修改不频繁 |
redis | 大 | 使用量大 | 修改频繁程度一般 |
ES | 超大 | 查询量大 | 修改频繁程度一般 |
不缓存 | - | 使用量小 | 修改频繁 |
若事件驱动更新缓存策略做得好,只要数据量不大,可忽略数据修改频繁程度的影响。
缓存方案并不唯一,以上仅为部分方案。具体实施过程需要结合实际情况,制定更适合的缓存方案。
Guava 缓存
创建缓存
public class CacheDemo {
private static final Cache<String, String> cache = CacheBuilder.newBuilder()
.concurrencyLevel(4)
.initialCapacity(10240)
.maximumSize(1024 * 1024L)
.maximumWeight(1024 * 1024 * 1024)
.weigher(new Weigher<String, String>() {
@Override
public int weigh(String key, String value) {
return key.getBytes().length + value.getBytes().length;
}
})
.expireAfterWrite(30, TimeUnit.MINUTES)
.expireAfterAccess(30, TimeUnit.MINUTES)
.removalListener(new RemovalListener<String, String>() {
@Override
public void onRemoval(RemovalNotification<String, String> notification) {
System.out.println(notification.getKey()+"-"+notification.getValue()+" is remove");
}
})
.recordStats()
.weakKeys()
.weakValues()
.softValues()
.build();
}
缓存操作
操作 | 含义 |
---|
cache.asMap(); | 将缓存转换成1个ConcurrentMap |
cache.cleanUp(); | 清空缓存 |
cache.get(K key, Callable<? extends V> loader); | 获取缓存,当缓存不存在时,则通Callable进行加载并返回。该操作是原子,会抛出ExecutionException异常 |
cache.getAllPresent(Iterable<?> keys); | 通过已存在的keys集合获取到一个固定长度的map集合 |
cache.getIfPresent(Object key); | 获取一个缓存,如果该缓存不存在则返回一个null值 |
cache.invalidate(Object key); | 通过key使value无效 |
cache.invalidateAll(); | 使所有的value无效 |
cache.invalidateAll(Iterable<?> keys); | 使keys集合中对应的value无效 |
cache.put(String key, Object value); | 向缓存中添加数据 |
cache.putAll(Map<? extends K, ? extends V> m); | 向级存中添加Map集合 |
cache.size(); | 缓存大小 |
cache.stats(); | 查看缓存命中结果 |