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)文章
詳解Java的JDBC API的存儲過程與SQL轉(zhuǎn)義語法的使用
這篇文章主要介紹了詳解Java的JDBC API的存儲過程與SQL轉(zhuǎn)義語法的使用,JDBC是Java用于連接使用各種數(shù)據(jù)庫的API,需要的朋友可以參考下2015-12-12
springMvc注解之@ResponseBody和@RequestBody詳解
本篇文章主要介紹了springMvc注解之@ResponseBody和@RequestBody詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-05-05
Java springboot Mongodb增刪改查代碼實例
這篇文章主要介紹了Java springboot Mongodb增刪改查代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-07-07
使用SpringAOP自定義權(quán)限控制注解的實現(xiàn)示例
本文主要介紹了使用SpringAOP自定義權(quán)限控制注解的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-08-08

