溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Redis的使用方法是怎么樣的

發(fā)布時(shí)間:2021-11-09 18:07:09 來(lái)源:億速云 閱讀:169 作者:柒染 欄目:大數(shù)據(jù)

本篇文章給大家分享的是有關(guān)Redis的使用方法是怎么樣的,小編覺(jué)得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說(shuō),跟著小編一起來(lái)看看吧。

設(shè)置過(guò)期時(shí)間、釋放資源

使用Redis做K-V存儲(chǔ),一定要注意過(guò)期時(shí)間的把控,任何K-V的存儲(chǔ)都要設(shè)置過(guò)期時(shí)間,不管多長(zhǎng)時(shí)間。一般在封裝Redis操作工具類時(shí)提供默認(rèn)使用系統(tǒng)公共超時(shí)時(shí)間的操作API,避免新手在使用時(shí)不設(shè)置過(guò)期時(shí)間,導(dǎo)致內(nèi)存的浪費(fèi)。另外,通過(guò)連接池 Jedis jedis = JedisPool.getResource(); 這樣獲取Redis連接最好使用try/finally塊,并且在finally塊中調(diào)用 jedis.close(); 將連接歸還給連接池,否則將會(huì)一直持有連接,很有可能導(dǎo)致在將來(lái)的某一時(shí)刻報(bào)拿不到連接的錯(cuò)。這也是之前某一個(gè)同事犯過(guò)的錯(cuò)導(dǎo)致生產(chǎn)bug!

 

緩存穿透

你以為Redis做緩存就萬(wàn)無(wú)一失嗎?就單純的遵循那種經(jīng)典操作嗎?(即:請(qǐng)求來(lái)了,先看緩存有沒(méi)有,有直接返回,沒(méi)有就查數(shù)據(jù)庫(kù),數(shù)據(jù)庫(kù)有的話先存緩存,然后返回,數(shù)據(jù)庫(kù)沒(méi)有就返回空)這樣就是Redis緩存的正確姿勢(shì)嗎?如果你這樣做,很可能疏忽一點(diǎn),那就是緩存穿透。如之前在項(xiàng)目中做的一個(gè)需求-頁(yè)面廣告可配置化自動(dòng)上下線(我在之前專門寫(xiě)過(guò)一篇文章介紹這個(gè)需求的一步步演進(jìn)過(guò)程,對(duì)Redis新手很有幫助,感興趣的可以去看看),簡(jiǎn)單的提一下吧,就是比如在支付完成的頁(yè)面大家都應(yīng)該見(jiàn)過(guò)吧,比如支付完成后的結(jié)果頁(yè),可能會(huì)彈出來(lái)一個(gè)紅包什么的,頁(yè)面下方的廣告位等,就是類似的這樣一個(gè)需求。因?yàn)檫@個(gè)頁(yè)面訪問(wèn)量很大,進(jìn)這個(gè)頁(yè)面就查這個(gè)廣告位的數(shù)據(jù),當(dāng)運(yùn)營(yíng)最近不想配置廣告了,這邊查到的是不是就是是空?。繑?shù)據(jù)庫(kù)也是空的,緩存也沒(méi)有數(shù)據(jù),那很多請(qǐng)求都來(lái),這樣就平白無(wú)故的造成了數(shù)據(jù)庫(kù)的壓力呀,多么的浪費(fèi)!如果是別的其他業(yè)務(wù),黑客鉆了空子,專門請(qǐng)求你系統(tǒng)根本不存在的數(shù)據(jù),請(qǐng)求多了,都打到數(shù)據(jù)庫(kù),是很有可能把你數(shù)據(jù)庫(kù)打死的。如果你在做需求的時(shí)候沒(méi)想到這一點(diǎn),那后續(xù)出了問(wèn)題,你就等著背鍋了。

怎么避免呢?

好辦,可以將數(shù)據(jù)庫(kù)也不存在的數(shù)據(jù)存?zhèn)€null值或一個(gè)空json(總之你自己約定好就行),也給放到Redis里,設(shè)置個(gè)較短的過(guò)期時(shí)間,下次再來(lái)取的時(shí)候看到是空就直接返回。另外,可以使用布隆過(guò)濾器做一層系統(tǒng)級(jí)的防護(hù),專門去攔截系統(tǒng)中根本不存在的key。

 

緩存雪崩

剛說(shuō)完緩存穿透,再聊聊緩存雪崩。比如你將用戶數(shù)據(jù)放到緩存里,當(dāng)某一時(shí)刻這些數(shù)據(jù)全部都過(guò)期了,大量請(qǐng)求都過(guò)來(lái),發(fā)現(xiàn)緩存無(wú)法命中,不就都去數(shù)據(jù)庫(kù)了嗎,數(shù)據(jù)庫(kù)一下子來(lái)這么多請(qǐng)求不就搞掛了嗎?解決辦法就是盡量是key的過(guò)期時(shí)間分散開(kāi),不要集中。在一個(gè)固定的過(guò)期時(shí)間上+一個(gè)隨機(jī)值,比如你設(shè)置的過(guò)期時(shí)間是5小時(shí),你可以加一個(gè)0-600秒的隨機(jī)值。

 

緩存并發(fā)

緩存失效時(shí)多個(gè)請(qǐng)求同時(shí)請(qǐng)求同一個(gè)key,都發(fā)現(xiàn)緩存中空了,都去查數(shù)據(jù)庫(kù),這不是浪費(fèi)嗎,正常一個(gè)去查就行了,查完放緩存別的請(qǐng)求直接從緩存拿就行了。這就是緩存并發(fā)問(wèn)題。當(dāng)請(qǐng)求非常的多的時(shí)候,會(huì)對(duì)數(shù)據(jù)庫(kù)造成很大的沖擊,也是有可能把數(shù)據(jù)庫(kù)搞掛的吧?怎么解決,可以對(duì)更新緩存的操作加鎖,使用synchronized嗎?不行,因?yàn)樯a(chǎn)上是分布式部署的,需要使用redis分布式鎖。

例如,當(dāng)緩存數(shù)據(jù)失效的時(shí)候,某一線程使用資源ID作為key嘗試加分布式鎖,加鎖成功的線程執(zhí)行更新緩存的操作將查到的數(shù)據(jù)放入緩存緩存中,其他線程就可以直接使用緩存數(shù)據(jù)了。

 

分布式鎖

正如上面所說(shuō),在集群部署的情況下synchronized就失效了,所以分布式鎖就派上用場(chǎng)了。常見(jiàn)的分布式鎖的實(shí)現(xiàn)方式有三種:基于數(shù)據(jù)庫(kù),基于Redis,基于Zookeeper。

Redis分布式鎖需要特別注意的點(diǎn)就是鎖的過(guò)期時(shí)間,如,使用redis的setnx命令,設(shè)置成功即表示拿到鎖,然后設(shè)置過(guò)期時(shí)間,命令執(zhí)行失敗的線程表示獲取鎖失敗。一定要注意鎖的過(guò)期時(shí)間的設(shè)置,有加鎖的操作,也要有解鎖的操作。如之前我們項(xiàng)目的一個(gè)臨時(shí)性的一個(gè)組團(tuán)競(jìng)走的活動(dòng),10人成團(tuán)競(jìng)走PK的活動(dòng),在組團(tuán)階段,用戶可以邀請(qǐng)朋友加入自己的團(tuán)。我們的團(tuán)數(shù)據(jù)是存放在Redis中的,包括每個(gè)團(tuán)的人數(shù)。當(dāng)用戶發(fā)起入團(tuán)操作時(shí),后臺(tái)邏輯會(huì)從redis取該團(tuán)的現(xiàn)有成員數(shù),如果小于10才能繼續(xù)走下面的邏輯。當(dāng)并發(fā)場(chǎng)景下,如團(tuán)長(zhǎng)分享給很多人入團(tuán)邀請(qǐng),這些人的入團(tuán)請(qǐng)求并發(fā)執(zhí)行的情況下很有可能能造成組團(tuán)人數(shù)超過(guò)10人的情況。因?yàn)樵诓l(fā)場(chǎng)景下,執(zhí)行獲取當(dāng)前團(tuán)成員數(shù)的這行代碼會(huì)被多個(gè)請(qǐng)求獲取到,比如臨界的時(shí)候,團(tuán)成員已經(jīng)有了9個(gè),同時(shí)來(lái)了倆入團(tuán)請(qǐng)求,如果不加控制,同時(shí)執(zhí)行讀取現(xiàn)有團(tuán)成員個(gè)數(shù)時(shí)都讀到的是9,然后都執(zhí)行入團(tuán)操作,就會(huì)造成團(tuán)成員超過(guò)10人的bug。

所以在入團(tuán)請(qǐng)求的邏輯上,要加分布式鎖,獲取到鎖才能執(zhí)行后續(xù)邏輯。因?yàn)楂@取鎖的操作是使用setnx命令,并沒(méi)有等待鎖的機(jī)制,我們需要在獲取鎖的邏輯加一個(gè)自旋,每隔一定時(shí)間嘗試一次獲取,超過(guò)一定時(shí)間后返回加鎖失敗。

public boolean tryLock(String lockKey,long expireTime){    long waitTime = 0;    //setIfAbsent使用的是redis的setnx方法    boolean success = redisTemplate.opsForValue().setIfAbsent(lockKey,"jingzouLock",            expireTime,TimeUnit.MILLISECONDS);    if(success==true){        return success;    }else{        while(success==false && waitTime <50000L){            success = redisTemplate.opsForValue().setIfAbsent(lockKey,"jingzouLock",                    expireTime, TimeUnit.MILLISECONDS);            try{                Thread.sleep(100);            }catch(Exception e){}            waitTime+=100L;        }    }    return success;}
 

另外,還需要遵循“解鈴還須系鈴人”的原則,誰(shuí)加的鎖誰(shuí)解,不然自己加的鎖,被別人解了也是會(huì)造成問(wèn)題的。例如,用戶A,請(qǐng)求入團(tuán),拿到分布式鎖,如果A因?yàn)槟承┰蛟阪i超時(shí)時(shí)間內(nèi)沒(méi)有執(zhí)行完代碼,鎖就過(guò)期自動(dòng)釋放了,如果此時(shí)B請(qǐng)求加入同一個(gè)團(tuán),拿到了分布式鎖,如果此時(shí)A請(qǐng)求執(zhí)行完了,釋放鎖了,但是釋放的是B的鎖,這樣也有可能造成團(tuán)人數(shù)超過(guò)10的bug。所以,設(shè)置分布式鎖時(shí)的value可以設(shè)置成不同的值,如A請(qǐng)求是用戶ID為12的用戶,設(shè)置分布式鎖的時(shí)候就value就可以用這個(gè)唯一的元素,當(dāng)解鎖的時(shí)候再驗(yàn)證value是12時(shí)才能執(zhí)行解鎖操作。

如上加鎖代碼,我們?cè)黾右粋€(gè)參數(shù)String value傳入動(dòng)態(tài)值,在上述場(chǎng)景中可以用用戶ID,代替我們寫(xiě)死的"jingzouLock"。然后在釋放鎖的方法里,我們先判斷value值,相同再執(zhí)行刪除。

public void releaseLock(String lockKey,String value){    String valueInRedis = redisTemplate.get(lockKey);    if(value.equals(valueInRedis)){        redisTemplate.delete(lockKey);    }}
 

還有一種場(chǎng)景需要考慮。當(dāng)Redis master發(fā)生故障,主備切換時(shí)往往會(huì)造成數(shù)據(jù)丟失,包括分布式鎖的Key-Value。這樣就會(huì)導(dǎo)致鎖間接的被釋放了,假如操作還沒(méi)執(zhí)行完,鎖被其他請(qǐng)求拿到了,分布式鎖就起不到作用了。

考慮到這方面的問(wèn)題,Redis官方提供了Redlock算法,以及相應(yīng)的開(kāi)源實(shí)現(xiàn)Redisson。用到分布式鎖的場(chǎng)景,大家可以直接使用 Redisson,非常方便,后期可能會(huì)寫(xiě)一寫(xiě)Redisson的技術(shù)干貨。

另外,如果系統(tǒng)對(duì)可靠性要求很高,如需用到分布式鎖,建議使用分布式鎖的另外實(shí)現(xiàn)方式,如:Zookeeper,etcd等。

以上就是Redis的使用方法是怎么樣的,小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見(jiàn)到或用到的。希望你能通過(guò)這篇文章學(xué)到更多知識(shí)。更多詳情敬請(qǐng)關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI