Redis+Caffeine多级缓存数据一致性解决方案
问题分析
通过Redis+Caffeine,似乎可以完成一级、二级缓存中数据的同步,如果在单节点项目中是没有问题的,但是,在分布式场景下是有问题的,看下图:
说明:
- 部署了2个transport-info微服务节点,每个微服务都有自己进程级的一级缓存,都共享同一个Redis作为二级缓存
- 假设,所有节点的一级和二级缓存都是空的,此时,用户通过节点1查询运单物流信息,在完成后,节点1的caffeine和Redis中都会有数据
- 接着,系统通过节点2更新了数据,此时节点2中的caffeine和Redis都是更新后的数据
- 用户还是进行查询动作,依然是通过节点1查询,此时查询到的将是旧的数据,也就是出现了一级缓存与二级缓存之间的数据不一致的问题
解决方案
如何解决该问题呢?可以通过消息的方式解决,就是任意一个节点数据更新了数据,发个消息出来,通知其他节点,其他节点接收到消息后,将自己caffeine中相应的数据删除即可。
关于消息的实现,可以采用RabbitMQ,也可以采用Redis的消息订阅发布来实现,在这里为了应用技术的多样化,所以采用Redis的订阅发布来实现。
方案概述
Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息
当有新消息通过 publish 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端。
Redis的订阅发布功能与传统的消息中间件(如:RabbitMQ)相比,相对轻量一些,针对数据准确和安全性要求没有那么高的场景可以直接使用。
代码实现
- 在RedisConfig增加订阅的配置:
/** * 配置订阅,用于解决Caffeine一致性的问题 * * @param connectionFactory 链接工厂 * @param listenerAdapter 消息监听器 * @return 消息监听容器 */ @Bean public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) { RedisMessageListenerContainer container = new RedisMessageListenerContainer(); container.setConnectionFactory(connectionFactory); container.addMessageListener(listenerAdapter, new ChannelTopic(CHANNEL_TOPIC)); return container; }
- 编写
RedisMessageListener
用于监听消息,删除caffeine中的数据。
/** * redis消息监听,解决Caffeine一致性的问题 */ @Slf4j @Component public class RedisMessageListener extends MessageListenerAdapter { @Resource private Cache<String, TransportInfoDTO> transportInfoCache; @Override public void onMessage(Message message, byte[] pattern) { // 获取到消息中的运单id String transportOrderId = Convert.toStr(message); log.info("redis消息监听缓存变更,运单id:{}", transportOrderId); // 将本jvm中的缓存删除掉 this.transportInfoCache.invalidate(transportOrderId); } }
- 更新数据后向redis发送消息:
@Resource private StringRedisTemplate stringRedisTemplate; @Override @CachePut(value = "transport-info", key = "#p0") public TransportInfoEntity saveOrUpdate(String transportOrderId, TransportInfoDetail infoDetail) { // 省略代码 // 清除缓存中的数据 // this.transportInfoCache.invalidate(transportOrderId); // Caffeine本地缓存一致性,发布订阅消息到redis,通知订阅者更新缓存 this.stringRedisTemplate.convertAndSend(RedisConfig.CHANNEL_TOPIC, transportOrderId); // 保存/更新到MongoDB return this.mongoTemplate.save(transportInfoEntity); }
总结
本文主要讲解了在使用Redis和Caffeine多级缓存时使用Redis的发布订阅模式来保证两级缓存的数据一致性。本地缓存是基于服务本地内存的,分布式系统中当缓存更新时,可能造成多个实例间的本地缓存不一致问题。可以使用RabbitMQ或者Redis的发布订阅来解决本地缓存不一致的问题。
以上就是Redis+Caffeine多级缓存数据一致性解决方案的详细内容,更多关于Redis Caffeine缓存数据一致性的资料请关注脚本之家其它相关文章!
相关文章
详解RedisTemplate下Redis分布式锁引发的系列问题
这篇文章主要介绍了详解RedisTemplate下Redis分布式锁引发的系列问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧2021-03-03Redis Cluster Pipeline导致的死锁问题解决
本文主要介绍了Redis Cluster Pipeline导致的死锁问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧2023-10-10redis.conf中使用requirepass不生效的原因及解决方法
本文主要介绍了如何启用requirepass,以及启用requirepass为什么不会生效,从代码层面分析了不生效的原因,以及解决方法,需要的朋友可以参考下2023-07-07
最新评论