本篇文章給大家分享的是有關(guān)redis的使用方法是怎么樣的,小編覺得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
延津網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián),延津網(wǎng)站設(shè)計(jì)制作,有大型網(wǎng)站制作公司豐富經(jīng)驗(yàn)。已為延津1000多家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\外貿(mào)網(wǎng)站制作要多少錢,請(qǐng)找那個(gè)售后服務(wù)好的延津做網(wǎng)站的公司定做!
使用Redis做K-V存儲(chǔ),一定要注意過期時(shí)間的把控,任何K-V的存儲(chǔ)都要設(shè)置過期時(shí)間,不管多長(zhǎng)時(shí)間。一般在封裝Redis操作工具類時(shí)提供默認(rèn)使用系統(tǒng)公共超時(shí)時(shí)間的操作API,避免新手在使用時(shí)不設(shè)置過期時(shí)間,導(dǎo)致內(nèi)存的浪費(fèi)。另外,通過連接池 Jedis jedis = JedisPool.getResource();
這樣獲取Redis連接最好使用try/finally
塊,并且在finally塊中調(diào)用 jedis.close()
; 將連接歸還給連接池,否則將會(huì)一直持有連接,很有可能導(dǎo)致在將來的某一時(shí)刻報(bào)拿不到連接的錯(cuò)。這也是之前某一個(gè)同事犯過的錯(cuò)導(dǎo)致生產(chǎn)bug!
你以為Redis做緩存就萬無一失嗎?就單純的遵循那種經(jīng)典操作嗎?(即:請(qǐng)求來了,先看緩存有沒有,有直接返回,沒有就查數(shù)據(jù)庫,數(shù)據(jù)庫有的話先存緩存,然后返回,數(shù)據(jù)庫沒有就返回空)這樣就是Redis緩存的正確姿勢(shì)嗎?如果你這樣做,很可能疏忽一點(diǎn),那就是緩存穿透。如之前在項(xiàng)目中做的一個(gè)需求-頁面廣告可配置化自動(dòng)上下線(我在之前專門寫過一篇文章介紹這個(gè)需求的一步步演進(jìn)過程,對(duì)Redis新手很有幫助,感興趣的可以去看看),簡(jiǎn)單的提一下吧,就是比如在支付完成的頁面大家都應(yīng)該見過吧,比如支付完成后的結(jié)果頁,可能會(huì)彈出來一個(gè)紅包什么的,頁面下方的廣告位等,就是類似的這樣一個(gè)需求。因?yàn)檫@個(gè)頁面訪問量很大,進(jìn)這個(gè)頁面就查這個(gè)廣告位的數(shù)據(jù),當(dāng)運(yùn)營(yíng)最近不想配置廣告了,這邊查到的是不是就是是空???數(shù)據(jù)庫也是空的,緩存也沒有數(shù)據(jù),那很多請(qǐng)求都來,這樣就平白無故的造成了數(shù)據(jù)庫的壓力呀,多么的浪費(fèi)!如果是別的其他業(yè)務(wù),黑客鉆了空子,專門請(qǐng)求你系統(tǒng)根本不存在的數(shù)據(jù),請(qǐng)求多了,都打到數(shù)據(jù)庫,是很有可能把你數(shù)據(jù)庫打死的。如果你在做需求的時(shí)候沒想到這一點(diǎn),那后續(xù)出了問題,你就等著背鍋了。
怎么避免呢?
好辦,可以將數(shù)據(jù)庫也不存在的數(shù)據(jù)存?zhèn)€null值或一個(gè)空json(總之你自己約定好就行),也給放到Redis里,設(shè)置個(gè)較短的過期時(shí)間,下次再來取的時(shí)候看到是空就直接返回。另外,可以使用布隆過濾器做一層系統(tǒng)級(jí)的防護(hù),專門去攔截系統(tǒng)中根本不存在的key。
剛說完緩存穿透,再聊聊緩存雪崩。比如你將用戶數(shù)據(jù)放到緩存里,當(dāng)某一時(shí)刻這些數(shù)據(jù)全部都過期了,大量請(qǐng)求都過來,發(fā)現(xiàn)緩存無法命中,不就都去數(shù)據(jù)庫了嗎,數(shù)據(jù)庫一下子來這么多請(qǐng)求不就搞掛了嗎?解決辦法就是盡量是key的過期時(shí)間分散開,不要集中。在一個(gè)固定的過期時(shí)間上+一個(gè)隨機(jī)值,比如你設(shè)置的過期時(shí)間是5小時(shí),你可以加一個(gè)0-600秒的隨機(jī)值。
緩存失效時(shí)多個(gè)請(qǐng)求同時(shí)請(qǐng)求同一個(gè)key,都發(fā)現(xiàn)緩存中空了,都去查數(shù)據(jù)庫,這不是浪費(fèi)嗎,正常一個(gè)去查就行了,查完放緩存別的請(qǐng)求直接從緩存拿就行了。這就是緩存并發(fā)問題。當(dāng)請(qǐng)求非常的多的時(shí)候,會(huì)對(duì)數(shù)據(jù)庫造成很大的沖擊,也是有可能把數(shù)據(jù)庫搞掛的吧?怎么解決,可以對(duì)更新緩存的操作加鎖,使用synchronized嗎?不行,因?yàn)樯a(chǎn)上是分布式部署的,需要使用redis分布式鎖。
例如,當(dāng)緩存數(shù)據(jù)失效的時(shí)候,某一線程使用資源ID作為key嘗試加分布式鎖,加鎖成功的線程執(zhí)行更新緩存的操作將查到的數(shù)據(jù)放入緩存緩存中,其他線程就可以直接使用緩存數(shù)據(jù)了。
正如上面所說,在集群部署的情況下synchronized就失效了,所以分布式鎖就派上用場(chǎng)了。常見的分布式鎖的實(shí)現(xiàn)方式有三種:基于數(shù)據(jù)庫,基于Redis,基于Zookeeper。
Redis分布式鎖需要特別注意的點(diǎn)就是鎖的過期時(shí)間,如,使用redis的setnx命令,設(shè)置成功即表示拿到鎖,然后設(shè)置過期時(shí)間,命令執(zhí)行失敗的線程表示獲取鎖失敗。一定要注意鎖的過期時(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ù)超過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í)來了倆入團(tuán)請(qǐng)求,如果不加控制,同時(shí)執(zhí)行讀取現(xiàn)有團(tuán)成員個(gè)數(shù)時(shí)都讀到的是9,然后都執(zhí)行入團(tuán)操作,就會(huì)造成團(tuán)成員超過10人的bug。
所以在入團(tuán)請(qǐng)求的邏輯上,要加分布式鎖,獲取到鎖才能執(zhí)行后續(xù)邏輯。因?yàn)楂@取鎖的操作是使用setnx命令,并沒有等待鎖的機(jī)制,我們需要在獲取鎖的邏輯加一個(gè)自旋,每隔一定時(shí)間嘗試一次獲取,超過一定時(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;}
另外,還需要遵循“解鈴還須系鈴人”的原則,誰加的鎖誰解,不然自己加的鎖,被別人解了也是會(huì)造成問題的。例如,用戶A,請(qǐng)求入團(tuán),拿到分布式鎖,如果A因?yàn)槟承┰蛟阪i超時(shí)時(shí)間內(nèi)沒有執(zhí)行完代碼,鎖就過期自動(dòng)釋放了,如果此時(shí)B請(qǐng)求加入同一個(gè)團(tuán),拿到了分布式鎖,如果此時(shí)A請(qǐng)求執(zhí)行完了,釋放鎖了,但是釋放的是B的鎖,這樣也有可能造成團(tuán)人數(shù)超過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,代替我們寫死的"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)致鎖間接的被釋放了,假如操作還沒執(zhí)行完,鎖被其他請(qǐng)求拿到了,分布式鎖就起不到作用了。
考慮到這方面的問題,Redis官方提供了Redlock算法,以及相應(yīng)的開源實(shí)現(xiàn)Redisson。用到分布式鎖的場(chǎng)景,大家可以直接使用 Redisson,非常方便,后期可能會(huì)寫一寫Redisson的技術(shù)干貨。
另外,如果系統(tǒng)對(duì)可靠性要求很高,如需用到分布式鎖,建議使用分布式鎖的另外實(shí)現(xiàn)方式,如:Zookeeper,etcd等。
以上就是Redis的使用方法是怎么樣的,小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見到或用到的。希望你能通過這篇文章學(xué)到更多知識(shí)。更多詳情敬請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。