✅如何保证本地缓存的一致性?

典型回答

✅本地缓存和分布式缓存有什么区别?

我们知道,本地缓存和分布式缓存相比有很多优点,但是最大的缺点就是存在着一致性的问题。于是就衍生出这个问题:如何保证本地缓存的一致性。

在回答这个问题之前,我先多说几句,这个问题挺好的,可以激发大家的解决问题的想法。但是后面我要说的不管哪个方案,如果真的是用在项目中,那么一定是不合理的。

因为本地缓存就是通过牺牲一致性来提升效率的,如果他能保证一致性,那么也就没分布式缓存什么事儿了。

虽然我们可以用接下来要介绍的手段在一定程度上解决,但是我要强调的是,很多方案你做了之后,本地缓存自身的优势可能也就没了。

所以,如果有一致性要求,那么就不要用本地缓存!!!

想要让本地缓存保持一致性,那么也就意味着,当一台机器上的本地缓存更新或者失效时,别的机器也要感知到,并且能及时处理。

首先, 有一个方案,就是我们给本地缓存记录一个版本,当某台机器的本地缓存更新之后,把他的最新的版本号以及缓存中的数据记录到数据库中,这样,当下次有请求请求到一个未更新过本地缓存的机器时,对比一下版本号,发现版本号旧了,那么就从数据库中更新一下本地缓存。

其次,借助配置中心。当某一个机器上的本地缓存发生变更之后,向配置中心做一次配置变更,然后通过配置中心把变更再推送到每一台机器上,大家监听配置变化做本地缓存的更新。

另外,借助MQ的广播消息,可以实现这个功能,当有实例需要更新本地缓存的时候,发一个MQ的广播消息,然后所有实例监听到这个广播消息后,各自更新自己的本地缓存。

1707114102684-5de37db0-d005-49df-8f08-3c6ee32f9308.png

✅RocketMQ怎么实现消息分发的?

除了数据库、广播消息和配置中心,也可以用redis,但是,这就有点脱裤子放屁了,用了本地缓存,还要去检查redis和数据库。。。

其实,真正的工作中,一般来说,对于本地缓存的使用,一般都是这样的

首先,肯定是要评估数据的变化频率,对于变化不频繁的数据,才会考虑放到本地缓存中。那种频繁更新的数据,其实并不适合放到本地缓存。比如数据库的库存,你见过哪个公司秒杀是在本地缓存做的?本地缓存这么快,咋不用呢?因为他就不适合啊。

还有就是,要提前评估下业务上能否接收不一致,以及能接受的不一致的时长。如果接受不了不一致,那就绝对不能用本地缓存。

如果能接受,那么就基于业务上能接受的时长设置失效时长,比如业务上可以接受10分钟的延迟,那么我们可以设置个8分钟的超时时间。这样到期之后这个缓存的内容就会自动失效。

在初始化缓存的时候,可以设置参数,如expireAfterAccess、expireAfterWrite、refreshAfterWrite,利用这些参数我们可以配置自动更新及自动失效。

自动失效

Cache<String, String> cache = Caffeine.newBuilder()
                .expireAfterWrite(5, TimeUnit.SECONDS) // 设置缓存项写入后的过期时间为5秒
                .build();

在自动失效后,查询本地缓存就会有一次cache miss,然后下次再查询就会去分布式缓存查询,然后再缓存到本地缓存中即可。这样就能保持最新数据了。

这是让缓存自动失效的方式,还有一种可以让本地缓存自动更新的方式。如Cffeine就支持可以定义一个refresh策略,他会定时的进行数据的刷新。

自动更新

Cache<String, String> cache = Caffeine.newBuilder()
                .refreshAfterWrite(5, TimeUnit.SECONDS) // 设置缓存项写入后的自动刷新时间为5秒
                .build(new CacheLoader<String, String>() { //定义一个CacheLoader,实现load方法。
                    @Override
                    public ListenableFuture<String> reload(String key, String oldValue) throws Exception {
                     return remoteCache.get(key);
                    }
               });

以上,会在达到缓存刷新的时间后,Caffeine会自动调用load方法进行数据读取并更新。

原文: https://www.yuque.com/hollis666/xkm7k3/ianhl677i5grnp0f