redis是一款內(nèi)存型數(shù)據(jù)庫,在開發(fā)工作中經(jīng)常用到,功能強(qiáng)大;
特別開一篇文章用來記錄一下它的常見用法,算是一種總結(jié);
它最主要的特點(diǎn)就是高可用的,速度快,分布式;有人說速度快,能有我本地的全局靜態(tài)變量快?但是在大型的項(xiàng)目中,多個(gè)服務(wù)器部署時(shí),其他服務(wù)實(shí)例節(jié)點(diǎn)如何獲取到你單個(gè)節(jié)點(diǎn)JVM中存儲(chǔ)的這個(gè)全局靜態(tài)變量?數(shù)據(jù)不一致怎么辦?如果只是用來讀取還好,但凡涉及到修改,都建議使用redis管理這部分常用而又準(zhǔn)備的數(shù)據(jù);
日常的工作可以是為某個(gè)業(yè)務(wù)功能開發(fā),也可以是單純的為實(shí)現(xiàn)某一項(xiàng)自認(rèn)為的技術(shù)開發(fā);
我總結(jié)了下一些常用的場景:
1、用于分布式的數(shù)據(jù)鎖
在一個(gè)告警的模塊,我們對單個(gè)對象的告警消息提示要做頻率限制,不然會(huì)導(dǎo)致頻繁推送單個(gè)對象的告警消息;例如:單個(gè)對象在10分鐘內(nèi),最多僅可提示一條消息;代碼片段如下:
//如果最近10分鐘內(nèi)已推送信息,則跳過
if (!redisTemplate.opsForValue().setIfAbsent(PARK_COUPON_ERROR_REDIS_KEY + order.getId(), 1, errmsgInterval, TimeUnit.SECONDS)) {
continue;
}
在一個(gè)抽獎(jiǎng)秒殺的功能中,我用redis來防止用戶頻繁點(diǎn)擊,超過活動(dòng)限定次數(shù)的抽獎(jiǎng),避免數(shù)據(jù)錯(cuò)亂的情況;
//避免 單用戶頻繁點(diǎn)擊
Boolean setIfAbsent = redisTemplate.opsForValue().setIfAbsent(userKey, LocalDateTime.now().toString(), Duration.ofSeconds(10));
if (ObjectUtil.isNotEmpty(setIfAbsent) && !setIfAbsent) {
log.info("單用戶加redis失敗 ,耗時(shí){}ms", stopwatch.getTotalTimeMillis());
throw new ServiceException("請勿頻繁點(diǎn)擊領(lǐng)券");
}
2、用于內(nèi)存存儲(chǔ)高頻調(diào)用數(shù)據(jù)
在redis中存儲(chǔ)高頻數(shù)據(jù)的作用非常大,可以有效地降低程序的處理時(shí)間;但是一般建議將該模塊設(shè)計(jì)巧妙一些,而不是一股腦將所有數(shù)據(jù)全部扔進(jìn)去;redis是內(nèi)存數(shù)據(jù)庫,比通過mysql查詢的效率往往高得多,因此可以提高程序的處理效率,且對于高頻的查詢數(shù)據(jù)放入redis中可以減少mysql的查詢壓力;
例如:秒殺活動(dòng)中的獎(jiǎng)品,打亂一次性存入隊(duì)列,后續(xù)抽獎(jiǎng)按順序取出即可;不用查詢MySQL
//將抽獎(jiǎng)的隨機(jī)性轉(zhuǎn)移到獎(jiǎng)品元素入隊(duì)順序的隨機(jī)性
Collections.shuffle(prizeList);
redisTemplate.opsForList().leftPushAll(LOTTERY_LOTTERY_PRIZE + lotteryId, prizeList);
例如:抽獎(jiǎng)活動(dòng)中,用戶再次點(diǎn)擊,判斷用戶是否還剩余抽獎(jiǎng)的權(quán)限,而不用查詢數(shù)據(jù)庫,對于這種限制一天一次,或者限制一周N次這樣的情況;
List<ChargingUserMarketingCoupon> userCouponList = chargingUserMarketingCouponService.list(new QueryWrapper<ChargingUserMarketingCoupon>().lambda().
eq(ChargingUserMarketingCoupon::getMarketingId, lotteryId).
eq(ChargingUserMarketingCoupon::getUserId, SecureUtil.getUserId()).
ge(ChargingUserMarketingCoupon::getCreateTime, start).
le(ChargingUserMarketingCoupon::getCreateTime, end));
String key = LOTTERY_USER_DELIVERY_NUM + ":" + lotteryId + ":" + DateUtil.format(start, "yyyyMMdd") + ":" + AuthUtil.getUserId();
redissonClient.getAtomicLong(key).set(userCouponList.size());
可以巧妙設(shè)計(jì)KEY,來實(shí)現(xiàn)對單個(gè)對象,某個(gè)日期中抽獎(jiǎng)次數(shù)的存儲(chǔ)和更新;
3、用于實(shí)現(xiàn)一些特殊的場景
例如:抽獎(jiǎng)活動(dòng),使用隊(duì)列一次性將獎(jiǎng)品全部入隊(duì),然后再出隊(duì)列,便不用查詢數(shù)據(jù)庫,且可以方便實(shí)現(xiàn)隊(duì)列出完就識別為抽獎(jiǎng)活動(dòng)結(jié)束的邏輯;
Object o = null;
try {
o = redisTemplate.opsForList().leftPop(LOTTERY_LOTTERY_PRIZE + lottery.getId(), 1, TimeUnit.SECONDS);
} catch (Exception e) {
e.printStackTrace();
throw new ServiceException("當(dāng)前抽獎(jiǎng)人數(shù)過多,請稍后重試!");
}
//仍然為NULL,則說明獎(jiǎng)品發(fā)放完畢,leftPop操作沒有元素
if (ObjectUtils.isEmpty(o)) {
throw new ServiceException("本次活動(dòng)獎(jiǎng)品已發(fā)放完畢,請關(guān)注下次活動(dòng)!");
}
這里需要注意,redis中的隊(duì)列元素在出隊(duì)完畢后,key會(huì)自動(dòng)刪除的;此時(shí)可能會(huì)報(bào)錯(cuò),而不是取出NULL數(shù)據(jù),開發(fā)時(shí)踩過坑;
就記錄這么多吧,要搬磚了;
總之一句話,就是合理使用redis的各大數(shù)據(jù)結(jié)構(gòu)和特性,來實(shí)現(xiàn)產(chǎn)品的特殊功能;文章來源:http://www.zghlxwxcb.cn/news/detail-708788.html
開發(fā)過程中要考慮周到,對于redis中數(shù)據(jù)存入后僅讀取的情況還好,對于要頻繁修改的數(shù)據(jù)還要考慮多線程并發(fā)等的問題;注意開發(fā)過程中可能的踩坑;文章來源地址http://www.zghlxwxcb.cn/news/detail-708788.html
到了這里,關(guān)于redis在日常開發(fā)工作中的常見用法的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!