前言
之前看过有赞TMC框架多级缓存实现,写过一篇文章:参照有赞TMC框架原理简单实现多级缓存,对于里面一些精髓还是没有学习到位,今天借助阿里开源的JetCache来进一步了解。
多级缓存是什么
有请gpt来讲讲~ 这个开发配置一个好助手,它对于内容的筛选会更细,不像某度引擎直接把一堆博客丢出来,很多还是模棱两可的,关键还有广告,哈哈~
它分析很详细,内存型的速度非常快的,但是它不能共享资源,分布式缓存速度比前面的慢,但是可以保证分布式环境数据一致性,持久性一般就是mysql这些关系型,特点就是容量大,但是不足就是并发超过200多的时候性能就会下降。
多级缓存应用场景
我们应用中一般会使用分布式缓存redis,它的不足是什么,它也有脆弱的时候,常见的面试题:缓存击穿、缓存穿透,这些场景也会让系统崩溃,还有热点key也会对特定的节点产生巨大压力,从而影响redis读取性能,从而多级缓存登场。
多级缓存思路
读写逻辑
数据储存逻辑
MultiLevelCache
循环塞入值,顶层是本地缓存,然后再是分布式缓存,他们之间是CompletableFuture,通过thenCombine进行调控,比如说本地跟分布式缓存不一致的话,会返回异常信息。
数据读取逻辑
顺序还是从本地到分布式下来读取。
checkResultAndFillUpperCache(key, i, holder);
这段代码,是强行刷里面TTL,就是保证在ttl时间内缓存内部是一致的。
一致性保证
这里的一致性有几方面:多级缓存一致性,不同节点的一致性
多级缓存它是怎么保证一致性的呢?来gpt同学回答一下
什么叫读写更新策略呢?跟redis一样的旁路更新策略,就是get获取的时候,如果多级缓存没有,那么从db读取之后更新的多级缓存里面,这解决第一个问题:多级缓存一致性。
第二个问题:不同节点的一致性,其实是靠redis发布订阅来解决,就是缓存key put、remove操作都会通过redis管道进行通知,然后同步不同的节点。如果有网关延迟怎么办,进行重试~
缓存击穿
来gpt同学再次回答一下问题
不得不讲人工智能确实很聪明,列了很多点,然后我们人工再去筛选想要的东西。
里面第一点提到了会使用分布式锁来避免缓存击穿,什么意思呢,比如说2个节点,1w并发,那么多级缓存都没有数据,这时,节点1会拿到分布式锁更新数据,其他的请求先卡住,等更新完了,节点2去更新自己的数据,这样即使并发再高,其实就2个请求打到数据库。
其他请求怎么等待呢?参考栅栏CountDownload。
热点key怎么监控
gpt同学:
public class HotKeyMonitor<K> implements CacheMonitor<K, Integer> {
private static final Logger logger = LoggerFactory.getLogger(HotKeyMonitor.class);
private final Map<K, AtomicInteger> counterMap = new ConcurrentHashMap<>();
private final Set<K> hotKeySet = new HashSet<>();
private final int threshold;
public HotKeyMonitor(int threshold) {
this.threshold = threshold;
}
@Override
public void onGet(String cacheName, K key, Integer value) {
if (value == null) {
return;
}
AtomicInteger counter = counterMap.computeIfAbsent(key, k -> new AtomicInteger());
if (counter.incrementAndGet() >= threshold) {
synchronized (hotKeySet) {
if (!hotKeySet.contains(key)) {
hotKeySet.add(key);
![]()logger.info("Hot key detected, cache: {}, key: {}, count: {}", cacheName, key, counter.get());
}
}
}
}
@Override
public void onPut(String cacheName, K key, Integer value) {
counterMap.remove(key);
synchronized (hotKeySet) {
hotKeySet.remove(key);
}
}
public Set<K> getHotKeySet() {
synchronized (hotKeySet) {
return new HashSet<>(hotKeySet);
}
}
}
那这个值怎么更新的?
那这个监控会在集群同步吗?
它在哪些方法会监控呢?
onGet请求会进行累加,通过事件异步,onPut会删除热点key
热点key监控之后,其实应该放到本地缓存去命中这样效率是最高的,避免redis网络请求,特别是big key问题。
总结
到这里我们从多级缓存的概念,到为什么要用多级缓存,然后从它的读写逻辑、一致性保证、缓存击穿、热点key监控,全面的对多级缓存有了深一层的理解,这里多亏了chatgpt同学的解答,还有我也去看了对应的源码,希望对各位读者也有帮助~
本文正在参加 人工智能创作者扶持计划