SpringBoot使用Redis同時(shí)執(zhí)行多條命令的實(shí)現(xiàn)方法
在 Spring Boot 項(xiàng)目中高效、合理地使用 Redis 同時(shí)執(zhí)行多條命令,可以顯著提升應(yīng)用性能。下面我將為你介紹幾種主要方式、它們的典型應(yīng)用場(chǎng)景,以及如何在 Spring Boot 中實(shí)現(xiàn)。
首先,我們來通過一個(gè)表格快速了解這幾種方式的特點(diǎn)和適用場(chǎng)景:
| 方式 | 原子性 | 主要優(yōu)勢(shì) | Spring Boot 中的典型應(yīng)用場(chǎng)景 |
|---|---|---|---|
| ?Pipeline (管道)?? | 否 | 大幅提升批量操作效率,減少網(wǎng)絡(luò)往返次數(shù) | 緩存預(yù)熱、批量數(shù)據(jù)導(dǎo)入導(dǎo)出、無依賴關(guān)系的批量查詢 |
| ?事務(wù) (Transaction)?? | 部分 | 命令隊(duì)列化,保證連續(xù)執(zhí)行(但不支持回滾) | 簡(jiǎn)單的原子操作序列,如庫(kù)存扣減、更新多個(gè)相關(guān)鍵 |
| ?Lua 腳本? | 是 | 復(fù)雜邏輯原子執(zhí)行,避免中間狀態(tài),性能高 | 分布式鎖、秒殺、需要原子性的復(fù)雜業(yè)務(wù)邏輯 |
1. Pipeline (管道)
Pipeline 允許客戶端將多個(gè)命令打包后一次性發(fā)送給 Redis 服務(wù)器,服務(wù)器依次執(zhí)行后再次將所有結(jié)果一次性返回給客戶端。這能顯著減少網(wǎng)絡(luò)往返次數(shù)(RTT)?,從而大幅提升吞吐量,尤其在高延遲網(wǎng)絡(luò)環(huán)境下效果明顯。
?Spring Boot 實(shí)現(xiàn)示例:??
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class RedisPipelineService {
private final StringRedisTemplate stringRedisTemplate;
// 構(gòu)造器注入
public RedisPipelineService(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
public void batchSetKeys(List<String> keys, List<String> values) {
// 使用 executePipelined 方法執(zhí)行管道操作
List<Object> results = stringRedisTemplate.executePipelined((RedisCallback<Object>) connection -> {
for (int i = 0; i < keys.size(); i++) {
connection.set(keys.get(i).getBytes(), values.get(i).getBytes());
}
return null; // 回調(diào)中返回 null
});
// results 包含每個(gè)命令的執(zhí)行結(jié)果
}
}
?使用場(chǎng)景:??
- ?緩存預(yù)熱?:應(yīng)用啟動(dòng)時(shí)批量加載熱點(diǎn)數(shù)據(jù)到 Redis。
- ?批量數(shù)據(jù)導(dǎo)入/導(dǎo)出?:例如從數(shù)據(jù)庫(kù)批量導(dǎo)入數(shù)據(jù)到 Redis,或從 Redis 批量獲取數(shù)據(jù)進(jìn)行處理。
- ?批量查詢無關(guān)聯(lián)數(shù)據(jù)?:一次性獲取多個(gè)不相關(guān)鍵的值,減少網(wǎng)絡(luò)開銷。
?注意事項(xiàng):??
- Pipeline 中的命令不具備原子性。
- 建議單次 Pipeline 命令數(shù)控制在合理范圍內(nèi)(如幾千條),避免服務(wù)器內(nèi)存壓力或客戶端長(zhǎng)時(shí)間阻塞。
- ?錯(cuò)誤處理?:某個(gè)命令失敗不會(huì)影響 Pipeline 中其他命令的執(zhí)行,需要在客戶端解析結(jié)果列表時(shí)逐一檢查。
2. 事務(wù) (Transaction)
Redis 事務(wù)通過 MULTI, EXEC, DISCARD, WATCH等命令實(shí)現(xiàn)。它允許將多個(gè)命令放入一個(gè)隊(duì)列,然后通過 EXEC命令原子性地順序執(zhí)行這些命令。
?Spring Boot 實(shí)現(xiàn)示例:??
Spring Data Redis 提供了 SessionCallback接口來支持在同一個(gè)連接中執(zhí)行多個(gè)操作,這對(duì)于事務(wù)至關(guān)重要。
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class RedisTransactionService {
private final RedisTemplate<String, Object> redisTemplate;
public RedisTransactionService(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
public List<Object> executeInTransaction() {
// 使用 execute 方法并傳遞 SessionCallback 來執(zhí)行事務(wù)
SessionCallback<List<Object>> sessionCallback = new SessionCallback<>() {
@Override
public List<Object> execute(org.springframework.data.redis.core.RedisOperations operations) {
operations.multi(); // 開啟事務(wù)
operations.opsForValue().set("key1", "value1");
operations.opsForValue().increment("counter");
operations.opsForSet().add("setKey", "member1");
return operations.exec(); // 執(zhí)行事務(wù)并返回結(jié)果
}
};
return redisTemplate.execute(sessionCallback);
}
}
?使用場(chǎng)景:??
- ?簡(jiǎn)單的原子操作序列?:需要確保一系列命令連續(xù)執(zhí)行,不被其他命令打斷,但不要求所有命令必須全部成功?(例如,扣減庫(kù)存后增加銷量)。
- ?結(jié)合
WATCH實(shí)現(xiàn)樂觀鎖?:監(jiān)控特定鍵,如秒殺場(chǎng)景中監(jiān)控庫(kù)存鍵,防止超賣。
// 結(jié)合 WATCH 的樂觀鎖示例
public boolean watchAndExecute(String key, String expectedValue, String newValue) {
return redisTemplate.execute(new SessionCallback<Boolean>() {
@Override
public Boolean execute(RedisOperations operations) {
operations.watch(key); // 監(jiān)視 key
String currentValue = (String) operations.opsForValue().get(key);
if (expectedValue.equals(currentValue)) {
operations.multi();
operations.opsForValue().set(key, newValue);
List<Object> execResult = operations.exec(); // 如果 execResult 為空,表示事務(wù)執(zhí)行失?。ㄦI被修改)
return execResult != null && !execResult.isEmpty();
}
operations.unwatch();
return false;
}
});
}
?注意事項(xiàng):??
- Redis 事務(wù)不支持回滾 (Rollback)?。如果在執(zhí)行過程中某個(gè)命令失敗,已執(zhí)行的命令不會(huì)回滾,后續(xù)命令仍會(huì)繼續(xù)執(zhí)行。
- ?錯(cuò)誤類型?:
- ?入隊(duì)錯(cuò)誤?(如命令語(yǔ)法錯(cuò)誤):在執(zhí)行
EXEC前,Redis 可能會(huì)檢查出錯(cuò)誤并放棄整個(gè)事務(wù)。 - ?運(yùn)行時(shí)錯(cuò)誤?(如數(shù)據(jù)類型操作錯(cuò)誤):在
EXEC后執(zhí)行中發(fā)生的錯(cuò)誤,Redis 會(huì)記錄錯(cuò)誤信息但不會(huì)中斷事務(wù)執(zhí)行。
- ?入隊(duì)錯(cuò)誤?(如命令語(yǔ)法錯(cuò)誤):在執(zhí)行
3. Lua 腳本
Redis 支持執(zhí)行 ?Lua 腳本。腳本中的所有命令會(huì)作為一個(gè)整體原子性地執(zhí)行,期間不會(huì)被其他命令打斷,是原子性最強(qiáng)的方案,同時(shí)還能減少網(wǎng)絡(luò)往返。
?Spring Boot 實(shí)現(xiàn)示例:??
RedisTemplate提供了 execute方法用于執(zhí)行 Lua 腳本。
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import java.util.Arrays;
@Service
public class RedisLuaService {
private final RedisTemplate<String, Object> redisTemplate;
public RedisLuaService(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
public String useLuaScript() {
// 定義 Lua 腳本字符串
String luaScript = """
local key1 = KEYS[1]
local value1 = ARGV[1]
redis.call('set', key1, value1)
local value = redis.call('get', key1)
redis.call('incr', 'counter')
return value
""";
DefaultRedisScript<String> script = new DefaultRedisScript<>();
script.setScriptText(luaScript);
script.setResultType(String.class); // 設(shè)置返回值類型
// 執(zhí)行腳本,傳入 keys 和 args 數(shù)組
String result = redisTemplate.execute(script, Arrays.asList("myKey"), "myValue");
return result; // 返回腳本執(zhí)行結(jié)果
}
}
?使用場(chǎng)景:??
?釋放分布式鎖?:確保判斷鎖標(biāo)識(shí)和刪除鎖是一個(gè)原子操作。
-- KEYS[1] 是鎖的key,ARGV[1]是當(dāng)前持有者的標(biāo)識(shí)
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
?秒殺/搶購(gòu)?:判斷庫(kù)存和扣減庫(kù)存需要原子性。
?復(fù)雜業(yè)務(wù)邏輯?:需要多個(gè)命令的中間結(jié)果進(jìn)行邏輯判斷。
?注意事項(xiàng):??
- ?腳本應(yīng)盡量簡(jiǎn)單快速?:執(zhí)行 Lua 腳本會(huì)阻塞 Redis,長(zhǎng)時(shí)間運(yùn)行的腳本會(huì)影響性能。
- ?注意腳本的復(fù)用?:Redis 會(huì)緩存編譯過的腳本,可以使用
EVALSHA通過腳本摘要來執(zhí)行,減少帶寬。DefaultRedisScript對(duì)象通常會(huì)被配置為單例,Spring 會(huì)智能地處理EVAL和EVALSHA。
如何選擇?
- ?追求極致性能,批量處理無關(guān)聯(lián)命令?:選擇 ?Pipeline。
- ?需要保證一系列命令連續(xù)執(zhí)行(簡(jiǎn)單原子性),且不介意無回滾?:選擇事務(wù)?(可配合 `WATCH**)。
- ?需要保證復(fù)雜操作原子性,或操作依賴于中間結(jié)果?:選擇 ?Lua 腳本。
集群環(huán)境特別注意
在 Redis Cluster 模式下,使用事務(wù) (Transaction) 或 Lua 腳本時(shí),?所有涉及的鍵必須在同一個(gè)哈希槽 (hash slot) 上,否則會(huì)報(bào)錯(cuò)。 可以通過 ?hash tag? 確保不同的鍵分配到同一個(gè)槽。
以上就是SpringBoot使用Redis同時(shí)執(zhí)行多條命令的實(shí)現(xiàn)方法的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot Redis同時(shí)執(zhí)行多條命令的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java中的lambda和stream實(shí)現(xiàn)排序
這篇文章主要介紹了Java中的lambda和stream實(shí)現(xiàn)排序,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09
java.lang.InterruptedException異常的問題解決
本文主要介紹了java.lang.InterruptedException異常的問題解決,這種異常通常意味著 Jenkins 任務(wù)在執(zhí)行過程中被中斷,這可能會(huì)導(dǎo)致任務(wù)失敗或中止,下面就來介紹一下解決方法,感興趣的可以了解一下2024-07-07
使用注解@Validated效驗(yàn)VO參數(shù)是否合規(guī)
這篇文章主要為大家介紹了使用注解@Validated效驗(yàn)VO參數(shù)是否合規(guī)過程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05
java開發(fā)分布式服務(wù)框架Dubbo調(diào)用過程
這篇文章主要為大家介紹了java開發(fā)分布式服務(wù)框架Dubbo調(diào)用過程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2021-11-11
SpringBoot+MybatisPlus+代碼生成器整合示例
這篇文章主要介紹了SpringBoot+MybatisPlus+代碼生成器整合示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03
基于Java開發(fā)一個(gè)Markdown到Word文檔轉(zhuǎn)換工具
本文介紹了一個(gè)用Java開發(fā)的Markdown到Word文檔轉(zhuǎn)換工具,該工具通過MarkdownToWordConverter類實(shí)現(xiàn),利用flexmark庫(kù)將Markdown內(nèi)容轉(zhuǎn)換為HTML,借助jsoup庫(kù)規(guī)范化HTML,再通過docx4j庫(kù)將處理后的HTML導(dǎo)入并保存為Word文檔,需要的朋友可以參考下2025-07-07
Java實(shí)現(xiàn)的斷點(diǎn)續(xù)傳功能的示例代碼
本篇文章主要介紹了Java實(shí)現(xiàn)的斷點(diǎn)續(xù)傳功能的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-02-02
IDEA 2019.2.2配置Maven3.6.2打開Maven項(xiàng)目出現(xiàn) Unable to import Maven
這篇文章主要介紹了IDEA 2019.2.2配置Maven3.6.2打開Maven項(xiàng)目出現(xiàn) Unable to import Maven project的問題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12

