首頁 > 軟體

Redis異常測試盤點分析

2022-05-27 18:07:52

Redis測試中的異常

在測試工作中,涉及到與 redis 互動的場景變的越來越多了。關於redis本身就不作贅述了,網上隨便搜,本人也做過一些整理。

今天只來覆盤一下,在測試過程中與 redis 的二三事兒。其中提到的案例是經過抽象化的,用作輔助說明作用,僅供參考。

一、更新 Key 異常

注意點:先刪除原 key 再存,還是直接覆蓋原 key?

比如:之前 A 服務每8小時去查詢一次資料庫,更新到快取裡去。後來需求調整,變成當資料庫裡有變動的時候就會傳送MQ訊息給服務 A,然後A就去全量拉取庫裡資料,再更新到快取。

開發小哥實現的是先刪除key再更新,那麼可能會導致這個時間如果有大量的請求進來,就不能命中快取。於是乎建議,當從資料庫拉來資料之後,可以先和redis中原來的key值進行對比,刪除多餘的快取,其他的覆蓋更新。

二、Key的刪除和丟失

注意點:考慮key被刪除,或者key丟失後對上游的影響。

比如:服務A 會同步一類資料到 redis,然後發訊息告訴 服務B。B 收到訊息後,拿到 redis 資料去找自己那邊 MongoDB裡的對應 key,做更新操作,若查不到key,就會刪除資料。

此時如果 redis 裡產生了資料丟失,key就不存在了,那麼同步過後,會導致 MongoDB 裡的資料被勿刪。

於是乎這裡建議方案是:redis 那邊涉及要刪除key的話,就更新key的值為空[],這時候 MongoDB 查詢到值為空的key,就去刪除對應資料。
另外,如果redis那邊key 丟失了,MongoDB這邊也別就刪資料了,去呼叫一個實時介面去查詢資料然後更新。

三、KEY 過期策略不當造成記憶體漏失

首先回顧一下 redis 中 ttl key指令:

  • 當 key 不存在時,返回 -2
  • 當 key 存在但沒有設定剩餘生存時間時,返回 -1
  • 否則,返回 key 的剩餘生存時間,單位是 s

通常,大多數業務用到redis 都會設定過期時間。接下來,瞭解一下 key 過期是如何清理的。

定期清理

Redis會定期主動淘汰一批已過期的key(隨機抽取一批key檢查)。

缺點:可能存在很多KEY已過期,仍未清理。

惰性清理

在獲取某個 key 的時候,redis 會檢查一下這個 key 如果設定了過期時間並且已經過期,就會刪除這個 key,不會返回任何東西。

缺點:如果存在很多未去查詢的過期key,就沒法走到惰性刪除,於是可能會有大量過期的key堆積在記憶體裡,導致記憶體耗盡。

一般來說,業務會惰性和定期清理配合使用。

記憶體淘汰機制

但是,如果定期清理漏掉了很多過期的key,然後你也沒及時去查,也就沒走惰性刪除。此時依舊有可能大量過期的key堆積在記憶體裡,導致記憶體耗盡。

這時候需要記憶體淘汰機制,有如下幾個:

  • noeviction:當記憶體不足以容納新寫入資料時,新寫入操作會報錯。這個一般很少用。
  • allkeys-lru:當記憶體不足以容納新寫入資料時,在鍵空間中,移除最近最少使用的key,這個是最常用的。
  • allkeys-random:當記憶體不足以容納新寫入資料時,在鍵空間中,隨機移除某個key。
  • volatile-lru:當記憶體不足以容納新寫入資料時,在設定了過期時間的鍵空間中,移除最近最少使用的key。
  • volatile-random:當記憶體不足以容納新寫入資料時,在設定了過期時間的鍵空間中,隨機移除某個key。
  • volatile-ttl:當記憶體不足以容納新寫入資料時,在設定了過期時間的鍵空間中,有更早過期時間的key優先移除。

以上可以作個瞭解。

四、查詢Redis異常時處理

很多時候,redis 只是做一個快取機制,如果redis異常或者未取到資料,是否有實時獲取資料的兜底方案(查介面 or 查庫?),需要考慮。

五、redis 穿透、擊穿、雪崩

穿透

使用者想要查詢一個資料,發現redis記憶體資料庫中沒有,也就是說沒有命中快取,也是會向持久層資料庫查詢,發現也沒有,那麼本次查詢失敗。
如果此時,使用者很多,高並行場景下都去查這個資料,由於快取都沒有命中,於是壓力直接打到持久層資料庫那裡,這就是快取穿透。

解決方案可以用布隆過濾器、返回空物件(設定過期時間)。

擊穿

快取擊穿,是指一個key非常熱點,在不停的扛著高並行,如果這個key失效了,在失效的瞬間,持續的並行量就會穿破快取,直接打到持久層資料庫,就像一個防禦牆被鑿開一個洞。

解決方案可以設定熱點資料永不過期、加互斥鎖等。

雪崩

是指在某一個時間段,快取集中過期失效,或者redis宕機了。

解決方案:

  • 事前:redis 高可用,主從+哨兵,redis cluster,避免全盤崩潰。
  • 事中:本地 ehcache 快取 + hystrix 限流&降級,避免 MySQL 被打死。
  • 事後:redis 持久化,一旦重啟,自動從磁碟上載入資料,快速恢復快取資料。

關於這3個,之前有過一篇整理:https://www.jb51.net/article/230997.htm

六、Redis死鎖

Redis鎖,小心使用不當造成鎖不能釋放,陷入死鎖。

目前常用的2種鎖:

SET Key UniqId Seconds

僅在單範例的場景下是安全的。如果不使用setnx+expire+del中間環節斷了仍可能造成死鎖;

如果不用SET Key UnixTimestamp Seconds NX,高並行下可能存在相同時間戳。

分散式Redis鎖:Redlock

此種方式比原先的單節點的方法更安全。

  • 安全性:在同一時間不允許多個Client同時持有鎖。
  • 活性死鎖:鎖最終應該能夠被釋放,即使Client端crash或者出現網路分割區(通常基於超時機制)。
  • 容錯性:只要超過半數Redis節點可用,鎖都能被正確獲取和釋放。

七、Redis持久化

當Redis資料需要長久有效時,需要考慮是否做RDB和AOF持久化,一般RDB和AOF配合使用,但做持久化,會影響效能。

目前接觸到的業務做持久化的很少見。比如有個推薦系統Redis資料是長久有效的,但卻為了響應快不影響效能,未做持久化,而採用了其他的降級方案Hbase,以及業務的兜底等。

八、快取與資料庫雙寫時的資料一致性

一般來說,就是如果你的系統不是嚴格要求快取+資料庫必須一致性的話,允許快取跟資料庫偶爾不一致的情況,那麼最後好不要做這個一致性方案。

如果實現這個方案,讀請求和寫請求序列化,串到一個記憶體佇列裡去,這樣就可以保證一定不會出現不一致的情況。

但是序列化之後,就會導致系統的吞吐量會大幅度的降低,用比正常情況下多幾倍的機器去支撐線上的一個請求。

還有一種適中的方式就是,就是先更新資料庫,然後再刪除快取。可能會暫時產生不一致的情況,但是發生的機率特別小。這時候通常並行寫資料庫和快取,可以加個事務,都寫成功才成功,有一個環節失敗了就回滾事務,全失敗。

關於雙寫一致性的問題,其實可以另起一個篇幅來說了,有興趣的可以網上搜尋一下,後續可能會再進行整理。

以上就是Redis異常測試盤點分析的詳細內容,更多關於Redis異常測試的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com