一眯亦、RBuckets批量操作
在SpringBoot項(xiàng)目中,通過(guò)RBuckets接口實(shí)現(xiàn)批量操作對(duì)個(gè)Bucket對(duì)象就缆,示例如下:
RBuckets buckets = redisson.getBuckets();
Map<String, V> loadedBuckets = buckets.get("myBucket1", "myBucket2", "myBucket3");
Map<String, Object> map = new HashMap<>();
map.put("myBucket1", new MyObject());
map.put("myBucket2", new MyObject());
// 利用Redis的事務(wù)特性涎永,同時(shí)保存所有的通用對(duì)象桶涯保,如果任意一個(gè)通用對(duì)象桶已經(jīng)存在則放棄保存其他所有數(shù)據(jù)晒来。
buckets.trySet(map);
// 同時(shí)保存全部通用對(duì)象桶钞诡。
buckets.set(map);
方法介紹:
- Map<String,V> get(String… keys):返回桶的key-value對(duì)。
- boolean trySet(Map<String,?> buckets):利用Redis的事務(wù)特性潜索,同時(shí)保存所有的通用對(duì)象桶臭增,如果任意一個(gè)通用對(duì)象桶已經(jīng)存在則放棄保存其他所有數(shù)據(jù)。
- void set(Map<String,?> buckets):同時(shí)保存全部通用對(duì)象桶竹习。
二、RBatch 批量操作
多個(gè)連續(xù)命令可以通過(guò)RBatch對(duì)象在一次網(wǎng)絡(luò)會(huì)話請(qǐng)求里合并發(fā)送列牺,這樣省去了產(chǎn)生多個(gè)請(qǐng)求消耗的時(shí)間和資源整陌。這在Redis中叫做管道。
RBatch管道功能就是REDIS的批量發(fā)送瞎领,實(shí)際上是客戶端的功能泌辫,與服務(wù)端無(wú)關(guān)。相當(dāng)于把多個(gè)請(qǐng)求的命令放在一個(gè)數(shù)據(jù)包通過(guò)TCP發(fā)送到服務(wù)端九默,然后客戶端再一次性讀取所有的命令回應(yīng)震放。管道技術(shù)最顯著的優(yōu)勢(shì)是提高了 redis 服務(wù)的性能。
/**
* 批量操作
*/
private void batchDemo() throws ExecutionException, InterruptedException {
Map<String, String> map = new HashMap<>();
map.put("abc", "testStr");
map.put("abcDemo", "redis");
redisUtils.setMassStrings(map);
log.info("String 測(cè)試數(shù)據(jù):{}", redisUtils.getStr("abc") + " "
+ redisUtils.getStr("abcDemo"));
RBatch batch = redisUtils.createBatch();
// 模擬購(gòu)物車場(chǎng)景驼修,真實(shí)場(chǎng)景中請(qǐng)?zhí)鎿Q店鋪ID shopId 和商品ID commodityId
String field = "shopId:commodityId";
// 把即將執(zhí)?的命令放進(jìn) RBatch
RMapAsync testMap = batch.getMap("customerId:"+ 32L);
// 更新value殿遂,并返回上一次的值
String commodityNum = "mapValue" + String.valueOf((int)(Math.random()*9 + 100));
log.info("當(dāng)前商品數(shù)量commodityNum是:{}", commodityNum);
testMap.putAsync(field, commodityNum);
testMap.putAsync("test2", "mapValue3");
testMap.putAsync("test2", "mapValue5");
testMap.putAsync("test:"+ String.valueOf((int)(Math.random()*900 + 100)), String.valueOf((int)(Math.random()*900 + 100)));
RAtomicLongAsync counter = batch.getAtomicLong("counter");
RFuture<Long> num = counter.incrementAndGetAsync();
// 執(zhí)行RBatch中的全部命令,并返回執(zhí)行結(jié)果
BatchResult result = batch.execute();
List list = result.getResponses();
log.info("Map Batch 執(zhí)行結(jié)果:{}", list);
log.info("計(jì)數(shù)器當(dāng)前值:{}", num.get());
}
執(zhí)行batchDemo()后乙各,控制臺(tái)打印結(jié)果如下:
StudyRedissonController - String 測(cè)試數(shù)據(jù):testStr redis
StudyRedissonController - 當(dāng)前商品數(shù)量commodityNum是:mapValue106
StudyRedissonController - Map Batch 執(zhí)行結(jié)果:[mapValue101, mapValue5, mapValue3, null, 8]
StudyRedissonController - 計(jì)數(shù)器當(dāng)前值:8
測(cè)試用例主要介紹了Hash墨礁,當(dāng)然RBatch還支持List、Set耳峦、對(duì)象桶恩静、隊(duì)列、發(fā)布訂閱等蹲坷。
順便介紹一下Redis中Map的使用場(chǎng)景
- 存儲(chǔ)結(jié)構(gòu)化的數(shù)據(jù)驶乾,比如 Java 中的對(duì)象邑飒。其實(shí) Java 中的對(duì)象也可以用 string 進(jìn)行存儲(chǔ),只需要將對(duì)象序列化成 json 字符串就可以级乐,但是如果這個(gè)對(duì)象的某個(gè)屬性更新比較頻繁的話疙咸,那么每次就需要重新將整個(gè)對(duì)象序列化存儲(chǔ),這樣消耗開(kāi)銷比較大唇牧『痹可如果用 hash 來(lái)存儲(chǔ)對(duì)象的每個(gè)屬性,那么每次只需要更新要更新的屬性就可以丐重。
- 購(gòu)物車場(chǎng)景腔召。以業(yè)務(wù)線+用戶id作為key,以店鋪編號(hào)+商品的id作為存儲(chǔ)的field扮惦,以選購(gòu)商品數(shù)量作為鍵值對(duì)的value臀蛛,這樣就構(gòu)成了購(gòu)物車的三個(gè)要素。
在集群模式下崖蜜,所有的命令會(huì)按各個(gè)槽所在的節(jié)點(diǎn)浊仆,篩選分配到各個(gè)節(jié)點(diǎn)并同時(shí)發(fā)送。每個(gè)節(jié)點(diǎn)返回的結(jié)果將會(huì)匯總到最終的結(jié)果列表里豫领。上述demo中用到的工具類如下:
@Component
public class RedisUtils {
/**
* 默認(rèn)緩存時(shí)間
*/
private static final Long DEFAULT_EXPIRED = 32000L;
/**
* 自動(dòng)裝配redisson client對(duì)象
*/
@Resource
private RedissonClient redissonClient;
/**
* 獲取getBuckets 對(duì)象
* @return RBuckets 對(duì)象
*/
public RBuckets getBuckets() {
return redissonClient.getBuckets();
}
/**
* 讀取緩存中的字符串抡柿,永久有效
* @param key 緩存key
* @return 字符串
*/
public String getStr(String key) {
RBucket<String> bucket = redissonClient.getBucket(key);
return bucket.get();
}
// ---------------- 批量操作 ------------------------
/**
* 獲取RBatch
* @return RBatch
*/
public RBatch createBatch() {
return redissonClient.createBatch();
}
/**
* 批量移除緩存
* @param keys key 對(duì)象
*/
public void deleteBatch(String... keys) {
if (null == keys) {
return;
}
redissonClient.getKeys().delete(keys);
}
/**
* 批量緩存字符串,缺點(diǎn):不可以設(shè)置過(guò)期時(shí)間
* @param map 緩存key-value
*/
public void setMassStrings(Map<String, String> map) {
if (MapUtils.isEmpty(map)) {
return;
}
RBuckets buckets = redissonClient.getBuckets();
// 同時(shí)保存全部通用對(duì)象桶等恐。
buckets.set(map);
}
/**
* 批量緩存字符串洲劣,支持過(guò)期
* @param map 緩存key-value
* @param leaseTime 緩存有效期,必傳
*/
public void setMassStrings(Map<String, String> map, long leaseTime) {
if (MapUtils.isEmpty(map)) {
return;
}
final long expireTime = leaseTime <= 0L ? DEFAULT_EXPIRED : leaseTime;
RBatch batch = redissonClient.createBatch();
map.forEach(new BiConsumer<String, String>() {
@Override
public void accept(String key, String value) {
batch.getBucket(key).setAsync(value, expireTime, TimeUnit.SECONDS);
}
});
batch.execute();
}
}