java如何實現(xiàn)高并發(fā)場景下三級緩存的數(shù)據(jù)一致性
下面代碼是一個使用Java和Redisson實現(xiàn)的三級緩存服務(wù),主要功能包括:
1.緩存結(jié)構(gòu):
- 本地緩存:使用Caffeine實現(xiàn),最大容量10,000,寫入后10分鐘過期
- 分布式緩存:使用Redisson的RMap結(jié)構(gòu)操作Redis
- 數(shù)據(jù)庫:作為最終數(shù)據(jù)源
2.數(shù)據(jù)讀取流程:
- 先查本地緩存,命中則返回
- 未命中則查Redis,命中則更新本地緩存并返回
- 仍未命中則獲取鎖,再次檢查兩級緩存(雙重檢查)
- 最后從數(shù)據(jù)庫讀取,更新兩級緩存后返回
3.數(shù)據(jù)更新流程:
- 使用分布式鎖保證寫操作原子性
- 先更新數(shù)據(jù)庫
- 刪除本地緩存和Redis緩存
- 通過Redis Pub/Sub發(fā)布緩存清除消息給集群內(nèi)其他節(jié)點
- 執(zhí)行延遲雙刪(100毫秒后再次刪除Redis緩存)
4.并發(fā)控制:
- 讀取時使用本地鎖(ReentrantLock)防止緩存擊穿
- 更新時使用Redisson分布式鎖(RLock)保證跨節(jié)點原子性
- 鎖使用完成后從ConcurrentHashMap中移除
5.集群同步:
- 使用Redis的RTopic實現(xiàn)消息發(fā)布訂閱
- 接收到清除消息時自動刪除本地緩存
- 確保集群內(nèi)各節(jié)點緩存一致性
該實現(xiàn)綜合運用了延遲雙刪、發(fā)布訂閱、鎖機制和TTL等多種策略,保障了高并發(fā)場景下三級緩存的數(shù)據(jù)一致性,尤其適合分布式微服務(wù)架構(gòu)。
實戰(zhàn)代碼:
import java.util.concurrent.*; import java.util.concurrent.locks.ReentrantLock; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.redisson.api.*; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.CacheBuilder; @Service public class CacheService { // 本地一級緩存(Caffeine) private final Cache<String, Object> localCache; // Redisson客戶端,用于分布式操作 private final RedissonClient redissonClient; // 鎖緩存,用于控制并發(fā) private final ConcurrentHashMap<String, ReentrantLock> lockMap = new ConcurrentHashMap<>(); // 延遲任務(wù)執(zhí)行器 private final ScheduledExecutorService scheduledExecutorService; // 主題訂閱,用于接收集群消息 private final RTopic cacheClearTopic; @Autowired public CacheService(RedissonClient redissonClient) { this.redissonClient = redissonClient; this.localCache = CacheBuilder.newBuilder() .maximumSize(10_000) .expireAfterWrite(10, TimeUnit.MINUTES) .build(); this.scheduledExecutorService = Executors.newScheduledThreadPool(5); this.cacheClearTopic = redissonClient.getTopic("cache:clear"); // 注冊消息監(jiān)聽器 cacheClearTopic.addListener(String.class, (channel, key) -> { localCache.invalidate(key); }); } // 讀取緩存 public Object get(String key) { // 1. 先查本地緩存 Object value = localCache.getIfPresent(key); if (value != null) { return value; } // 2. 本地緩存未命中,查Redis RMap<String, Object> redisMap = redissonClient.getMap("cache"); value = redisMap.get(key); if (value != null) { localCache.put(key, value); return value; } // 3. Redis未命中,查數(shù)據(jù)庫 ReentrantLock lock = lockMap.computeIfAbsent(key, k -> new ReentrantLock()); lock.lock(); try { // 雙重檢查 value = localCache.getIfPresent(key); if (value != null) { return value; } value = redisMap.get(key); if (value != null) { localCache.put(key, value); return value; } // 從數(shù)據(jù)庫讀取 value = readFromDatabase(key); if (value != null) { // 放入Redis并設(shè)置TTL redisMap.put(key, value, 300, TimeUnit.SECONDS); // 放入本地緩存 localCache.put(key, value); } return value; } finally { lock.unlock(); lockMap.remove(key); } } // 更新數(shù)據(jù) public void update(String key, Object value) { // 使用分布式鎖保證寫操作的原子性 RLock lock = redissonClient.getLock("writeLock:" + key); lock.lock(); try { // 1. 更新數(shù)據(jù)庫 boolean success = updateDatabase(key, value); if (success) { // 2. 先刪除本地緩存 localCache.invalidate(key); // 3. 刪除Redis緩存 RMap<String, Object> redisMap = redissonClient.getMap("cache"); redisMap.remove(key); // 4. 發(fā)布清除緩存的消息到集群 cacheClearTopic.publish(key); // 5. 延遲雙刪 scheduledExecutorService.schedule(() -> { redisMap.remove(key); }, 100, TimeUnit.MILLISECONDS); } } finally { lock.unlock(); } } // 從數(shù)據(jù)庫讀取數(shù)據(jù)(示例方法) private Object readFromDatabase(String key) { // 實際實現(xiàn)中會查詢數(shù)據(jù)庫 return "data_from_db_" + key; } // 更新數(shù)據(jù)庫(示例方法) private boolean updateDatabase(String key, Object value) { // 實際實現(xiàn)中會更新數(shù)據(jù)庫 return true; } }
redisson配置
import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RedissonConfig { @Bean public RedissonClient redissonClient() { Config config = new Config(); // 單機模式配置 config.useSingleServer() .setAddress("redis://localhost:6379") .setConnectionMinimumIdleSize(5) .setConnectionPoolSize(50); // 集群模式配置示例 /* config.useClusterServers() .addNodeAddress("redis://node1:6379", "redis://node2:6379") .setScanInterval(2000) .setMasterConnectionMinimumIdleSize(10) .setMasterConnectionPoolSize(64) .setSlaveConnectionMinimumIdleSize(10) .setSlaveConnectionPoolSize(64); */ return Redisson.create(config); } }
到此這篇關(guān)于java如何實現(xiàn)高并發(fā)場景下三級緩存的數(shù)據(jù)一致性的文章就介紹到這了,更多相關(guān)java三級緩存內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring之借助Redis設(shè)計一個簡單訪問計數(shù)器的示例
本篇文章主要介紹了Spring之借助Redis設(shè)計一個簡單訪問計數(shù)器的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-06-06Java tomcat環(huán)境變量及idea配置解析
這篇文章主要介紹了Java tomcat環(huán)境變量及idea配置解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-12-12Java中使用Hutool的DsFactory操作多數(shù)據(jù)源的實現(xiàn)
在Java開發(fā)中,管理多個數(shù)據(jù)源是一項常見需求,Hutool作為一個全能的Java工具類庫,提供了DsFactory工具,幫助開發(fā)者便捷地操作多數(shù)據(jù)源,感興趣的可以了解一下2024-09-09springboot通過spel結(jié)合aop實現(xiàn)動態(tài)傳參的案例
SpEl 是Spring框架中的一個利器,Spring通過SpEl能在運行時構(gòu)建復雜表達式、存取對象屬性、對象方法調(diào)用等,今天通過本文給大家介紹springboot?spel結(jié)合aop實現(xiàn)動態(tài)傳參,需要的朋友可以參考下2022-07-07