Redis異常測試盤點分析
Redis測試中的異常
在測試工作中,涉及到與 redis 交互的場景變的越來越多了。關(guān)于redis本身就不作贅述了,網(wǎng)上隨便搜,本人也做過一些整理。
今天只來復(fù)盤一下,在測試過程中與 redis 的二三事兒。其中提到的案例是經(jīng)過抽象化的,用作輔助說明作用,僅供參考。
一、更新 Key 異常
注意點:先刪除原 key 再存,還是直接覆蓋原 key?
比如:之前 A 服務(wù)每8小時去查詢一次數(shù)據(jù)庫,更新到緩存里去。后來需求調(diào)整,變成當(dāng)數(shù)據(jù)庫里有變動的時候就會發(fā)送MQ消息給服務(wù) A,然后A就去全量拉取庫里數(shù)據(jù),再更新到緩存。
開發(fā)小哥實現(xiàn)的是先刪除key再更新,那么可能會導(dǎo)致這個時間如果有大量的請求進(jìn)來,就不能命中緩存。于是乎建議,當(dāng)從數(shù)據(jù)庫拉來數(shù)據(jù)之后,可以先和redis中原來的key值進(jìn)行對比,刪除多余的緩存,其他的覆蓋更新。
二、Key的刪除和丟失
注意點:考慮key被刪除,或者key丟失后對上游的影響。
比如:服務(wù)A 會同步一類數(shù)據(jù)到 redis,然后發(fā)消息告訴 服務(wù)B。B 收到消息后,拿到 redis 數(shù)據(jù)去找自己那邊 MongoDB里的對應(yīng) key,做更新操作,若查不到key,就會刪除數(shù)據(jù)。
此時如果 redis 里產(chǎn)生了數(shù)據(jù)丟失,key就不存在了,那么同步過后,會導(dǎo)致 MongoDB 里的數(shù)據(jù)被勿刪。
于是乎這里建議方案是:redis 那邊涉及要刪除key的話,就更新key的值為空[],這時候 MongoDB 查詢到值為空的key,就去刪除對應(yīng)數(shù)據(jù)。
另外,如果redis那邊key 丟失了,MongoDB這邊也別就刪數(shù)據(jù)了,去調(diào)用一個實時接口去查詢數(shù)據(jù)然后更新。
三、KEY 過期策略不當(dāng)造成內(nèi)存泄漏
首先回顧一下 redis 中 ttl key指令:
- 當(dāng) key 不存在時,返回 -2
- 當(dāng) key 存在但沒有設(shè)置剩余生存時間時,返回 -1
- 否則,返回 key 的剩余生存時間,單位是 s
通常,大多數(shù)業(yè)務(wù)用到redis 都會設(shè)置過期時間。接下來,了解一下 key 過期是如何清理的。
定期清理
Redis會定期主動淘汰一批已過期的key(隨機(jī)抽取一批key檢查)。
缺點:可能存在很多KEY已過期,仍未清理。
惰性清理
在獲取某個 key 的時候,redis 會檢查一下這個 key 如果設(shè)置了過期時間并且已經(jīng)過期,就會刪除這個 key,不會返回任何東西。
缺點:如果存在很多未去查詢的過期key,就沒法走到惰性刪除,于是可能會有大量過期的key堆積在內(nèi)存里,導(dǎo)致內(nèi)存耗盡。
一般來說,業(yè)務(wù)會惰性和定期清理配合使用。
內(nèi)存淘汰機(jī)制
但是,如果定期清理漏掉了很多過期的key,然后你也沒及時去查,也就沒走惰性刪除。此時依舊有可能大量過期的key堆積在內(nèi)存里,導(dǎo)致內(nèi)存耗盡。
這時候需要內(nèi)存淘汰機(jī)制,有如下幾個:
- noeviction:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時,新寫入操作會報錯。這個一般很少用。
- allkeys-lru:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時,在鍵空間中,移除最近最少使用的key,這個是最常用的。
- allkeys-random:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時,在鍵空間中,隨機(jī)移除某個key。
- volatile-lru:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時,在設(shè)置了過期時間的鍵空間中,移除最近最少使用的key。
- volatile-random:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時,在設(shè)置了過期時間的鍵空間中,隨機(jī)移除某個key。
- volatile-ttl:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時,在設(shè)置了過期時間的鍵空間中,有更早過期時間的key優(yōu)先移除。
以上可以作個了解。
四、查詢Redis異常時處理
很多時候,redis 只是做一個緩存機(jī)制,如果redis異?;蛘呶慈〉綌?shù)據(jù),是否有實時獲取數(shù)據(jù)的兜底方案(查接口 or 查庫?),需要考慮。
五、redis 穿透、擊穿、雪崩
穿透
用戶想要查詢一個數(shù)據(jù),發(fā)現(xiàn)redis內(nèi)存數(shù)據(jù)庫中沒有,也就是說沒有命中緩存,也是會向持久層數(shù)據(jù)庫查詢,發(fā)現(xiàn)也沒有,那么本次查詢失敗。
如果此時,用戶很多,高并發(fā)場景下都去查這個數(shù)據(jù),由于緩存都沒有命中,于是壓力直接打到持久層數(shù)據(jù)庫那里,這就是緩存穿透。
解決方案可以用布隆過濾器、返回空對象(設(shè)置過期時間)。
擊穿
緩存擊穿,是指一個key非常熱點,在不停的扛著高并發(fā),如果這個key失效了,在失效的瞬間,持續(xù)的并發(fā)量就會穿破緩存,直接打到持久層數(shù)據(jù)庫,就像一個防御墻被鑿開一個洞。
解決方案可以設(shè)置熱點數(shù)據(jù)永不過期、加互斥鎖等。
雪崩
是指在某一個時間段,緩存集中過期失效,或者redis宕機(jī)了。
解決方案:
- 事前:redis 高可用,主從+哨兵,redis cluster,避免全盤崩潰。
- 事中:本地 ehcache 緩存 + hystrix 限流&降級,避免 MySQL 被打死。
- 事后:redis 持久化,一旦重啟,自動從磁盤上加載數(shù)據(jù),快速恢復(fù)緩存數(shù)據(jù)。
關(guān)于這3個,之前有過一篇整理:http://www.dbjr.com.cn/article/230997.htm
六、Redis死鎖
Redis鎖,小心使用不當(dāng)造成鎖不能釋放,陷入死鎖。
目前常用的2種鎖:
SET Key UniqId Seconds
僅在單實例的場景下是安全的。如果不使用setnx+expire+del中間環(huán)節(jié)斷了仍可能造成死鎖;
如果不用SET Key UnixTimestamp Seconds NX,高并發(fā)下可能存在相同時間戳。
分布式Redis鎖:Redlock
此種方式比原先的單節(jié)點的方法更安全。
- 安全性:在同一時間不允許多個Client同時持有鎖。
- 活性死鎖:鎖最終應(yīng)該能夠被釋放,即使Client端crash或者出現(xiàn)網(wǎng)絡(luò)分區(qū)(通?;诔瑫r機(jī)制)。
- 容錯性:只要超過半數(shù)Redis節(jié)點可用,鎖都能被正確獲取和釋放。
七、Redis持久化
當(dāng)Redis數(shù)據(jù)需要長久有效時,需要考慮是否做RDB和AOF持久化,一般RDB和AOF配合使用,但做持久化,會影響性能。
目前接觸到的業(yè)務(wù)做持久化的很少見。比如有個推薦系統(tǒng)Redis數(shù)據(jù)是長久有效的,但卻為了響應(yīng)快不影響性能,未做持久化,而采用了其他的降級方案Hbase,以及業(yè)務(wù)的兜底等。
八、緩存與數(shù)據(jù)庫雙寫時的數(shù)據(jù)一致性
一般來說,就是如果你的系統(tǒng)不是嚴(yán)格要求緩存+數(shù)據(jù)庫必須一致性的話,允許緩存跟數(shù)據(jù)庫偶爾不一致的情況,那么最后好不要做這個一致性方案。
如果實現(xiàn)這個方案,讀請求和寫請求串行化,串到一個內(nèi)存隊列里去,這樣就可以保證一定不會出現(xiàn)不一致的情況。
但是串行化之后,就會導(dǎo)致系統(tǒng)的吞吐量會大幅度的降低,用比正常情況下多幾倍的機(jī)器去支撐線上的一個請求。
還有一種適中的方式就是,就是先更新數(shù)據(jù)庫,然后再刪除緩存??赡軙簳r產(chǎn)生不一致的情況,但是發(fā)生的幾率特別小。這時候通常并行寫數(shù)據(jù)庫和緩存,可以加個事務(wù),都寫成功才成功,有一個環(huán)節(jié)失敗了就回滾事務(wù),全失敗。
關(guān)于雙寫一致性的問題,其實可以另起一個篇幅來說了,有興趣的可以網(wǎng)上搜索一下,后續(xù)可能會再進(jìn)行整理。
以上就是Redis異常測試盤點分析的詳細(xì)內(nèi)容,更多關(guān)于Redis異常測試的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
了解redis中RDB結(jié)構(gòu)_動力節(jié)點Java學(xué)院整理
這篇文章主要為大家詳細(xì)介紹了redis中RDB結(jié)構(gòu),具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08Redis和Nginx實現(xiàn)限制接口請求頻率的示例
限流就是限制API訪問頻率,當(dāng)訪問頻率超過某個閾值時進(jìn)行拒絕訪問等操作,本文主要介紹了Redis和Nginx實現(xiàn)限制接口請求頻率的示例,具有一定的參考價值,感興趣的可以了解一下2024-02-02Redis Sentinel實現(xiàn)高可用配置的詳細(xì)步驟
這篇文章主要介紹了Redis Sentinel實現(xiàn)高可用配置的詳細(xì)步驟,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-09-09RedisTemplate常用操作方法總結(jié)(set、hash、list、string等)
本文主要介紹了RedisTemplate常用操作方法總結(jié),主要包括了6種常用方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05