Redis+Caffeine多級緩存數據一致性解決方案
問題分析
通過Redis+Caffeine,似乎可以完成一級、二級緩存中數據的同步,如果在單節(jié)點項目中是沒有問題的,但是,在分布式場景下是有問題的,看下圖:
說明:
- 部署了2個transport-info微服務節(jié)點,每個微服務都有自己進程級的一級緩存,都共享同一個Redis作為二級緩存
- 假設,所有節(jié)點的一級和二級緩存都是空的,此時,用戶通過節(jié)點1查詢運單物流信息,在完成后,節(jié)點1的caffeine和Redis中都會有數據
- 接著,系統(tǒng)通過節(jié)點2更新了數據,此時節(jié)點2中的caffeine和Redis都是更新后的數據
- 用戶還是進行查詢動作,依然是通過節(jié)點1查詢,此時查詢到的將是舊的數據,也就是出現(xiàn)了一級緩存與二級緩存之間的數據不一致的問題
解決方案
如何解決該問題呢?可以通過消息的方式解決,就是任意一個節(jié)點數據更新了數據,發(fā)個消息出來,通知其他節(jié)點,其他節(jié)點接收到消息后,將自己caffeine中相應的數據刪除即可。
關于消息的實現(xiàn),可以采用RabbitMQ,也可以采用Redis的消息訂閱發(fā)布來實現(xiàn),在這里為了應用技術的多樣化,所以采用Redis的訂閱發(fā)布來實現(xiàn)。
方案概述
Redis 發(fā)布訂閱(pub/sub)是一種消息通信模式:發(fā)送者(pub)發(fā)送消息,訂閱者(sub)接收消息
當有新消息通過 publish 命令發(fā)送給頻道 channel1 時, 這個消息就會被發(fā)送給訂閱它的三個客戶端。
Redis的訂閱發(fā)布功能與傳統(tǒng)的消息中間件(如:RabbitMQ)相比,相對輕量一些,針對數據準確和安全性要求沒有那么高的場景可以直接使用。
代碼實現(xiàn)
- 在RedisConfig增加訂閱的配置:
/** * 配置訂閱,用于解決Caffeine一致性的問題 * * @param connectionFactory 鏈接工廠 * @param listenerAdapter 消息監(jiān)聽器 * @return 消息監(jiān)聽容器 */ @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
用于監(jiān)聽消息,刪除caffeine中的數據。
/** * redis消息監(jiān)聽,解決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消息監(jiān)聽緩存變更,運單id:{}", transportOrderId); // 將本jvm中的緩存刪除掉 this.transportInfoCache.invalidate(transportOrderId); } }
- 更新數據后向redis發(fā)送消息:
@Resource private StringRedisTemplate stringRedisTemplate; @Override @CachePut(value = "transport-info", key = "#p0") public TransportInfoEntity saveOrUpdate(String transportOrderId, TransportInfoDetail infoDetail) { // 省略代碼 // 清除緩存中的數據 // this.transportInfoCache.invalidate(transportOrderId); // Caffeine本地緩存一致性,發(fā)布訂閱消息到redis,通知訂閱者更新緩存 this.stringRedisTemplate.convertAndSend(RedisConfig.CHANNEL_TOPIC, transportOrderId); // 保存/更新到MongoDB return this.mongoTemplate.save(transportInfoEntity); }
總結
本文主要講解了在使用Redis和Caffeine多級緩存時使用Redis的發(fā)布訂閱模式來保證兩級緩存的數據一致性。本地緩存是基于服務本地內存的,分布式系統(tǒng)中當緩存更新時,可能造成多個實例間的本地緩存不一致問題??梢允褂肦abbitMQ或者Redis的發(fā)布訂閱來解決本地緩存不一致的問題。
以上就是Redis+Caffeine多級緩存數據一致性解決方案的詳細內容,更多關于Redis Caffeine緩存數據一致性的資料請關注腳本之家其它相關文章!
相關文章
Redis 8種基本數據類型及常用命令和數據類型的應用場景小結
Redis是一種基于內存操作的數據庫,其中多虧于高效的數據結構,本文主要介紹了Redis 8種基本數據類型及常用命令和數據類型的應用場景小結,具有一定的參考價值,感興趣的可以了解一下2024-03-03詳解使用Redis SETNX 命令實現(xiàn)分布式鎖
本篇文章主要介紹了詳解使用Redis SETNX 命令實現(xiàn)分布式鎖,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-01-01解讀Redis秒殺優(yōu)化方案(阻塞隊列+基于Stream流的消息隊列)
該文章介紹了使用Redis的阻塞隊列和Stream流的消息隊列來優(yōu)化秒殺系統(tǒng)的方案,通過將秒殺流程拆分為兩條流水線,使用Redis緩存緩解數據庫壓力,并結合Lua腳本進行原子性判斷,使用阻塞隊列和消息隊列異步處理訂單,有效提高了系統(tǒng)的并發(fā)處理能力和可用性2025-02-02