作為一個測試同學,被測系統架構中有使用到 redis 嗎?對 redis 常見的故障有了解嗎?又是如何進行測試的呢?
更多內容可以學習《測試工程師 Python 工具開發實戰》書籍、《大話效能測試 JMeter 實戰》書籍
針對常見的 redis 面試問題,怎樣才算一個高質量的回答呢,回答思路一般包括
- 問題的型別是什麼?
- 通常會在什麼場景發生?(透過使用被測系統的具體場景進行描述,會更加真實可信)
- 發生的根本原因是什麼?(redis 等使用較廣泛的開源軟體,普通問題基本都會有相應的解決方案,發生問題的原因一般是因為使用不當導致)
- 發生前期要如何避免?(編碼時針對問題使用那種方案?測試過程中如何發現該類問題等)
- 發生後如何進行搶救以及各種方案有什麼優缺點等?
一般對於測試同學來說,面試過程中不需要去了解 redis 的原始碼和實現方式(當然瞭解更好),但是對於相應故障場景及原因、解決方案是需要有所瞭解的。
常見問題
1.快取雪崩
定義:
由於原來的快取資料失效,而新的快取資料還沒有更新到快取中時,導致所有請求都直接去查詢資料庫,對資料庫 CPU 和記憶體造成巨大壓力,嚴重的會造成資料庫當機。從而形成一系列連鎖反應,造成整個系統崩潰。場景:
比如設定定時過期的資料使用定時任務重新整理到快取就有有可能造成快取雪崩。假設現在有一個秒殺系統,如果所有首頁的 Key 失效時間都是 12 小時,中午 12 點重新整理的,零點有個秒殺活動大量使用者湧入,假設當時每秒 6000 個請求,本來快取在可以扛住每秒 5000 個請求,但是快取當時所有的 Key 都失效了。此時 1 秒 6000 個請求全部落資料庫,資料庫必然扛不住,它會報一下警,真實情況可能 DBA 都沒反應過來就直接掛了。此時,如果沒用什麼特別的方案來處理這個故障,DBA 很著急,重啟資料庫,但是資料庫立馬又被新的流量給打死了,這會導致一系列的服務請求資料庫都會報錯,形成雪崩效應。這就是我理解的快取雪崩。解決方案:
批次往 Redis 存資料的時候,把每個 Key 的失效時間設定為隨機值,這樣可以保證快取中的資料不會在同一時間大面積失效(這種方式一般透過看程式碼或者詢問開發得知失效時間,可透過登入 redis 服務查詢即可測試);
或者設定熱點資料永遠不過期,此時如果有更新操作就更新快取就好了(比如運維更新了首頁商品,那你刷下快取就完事了,不要設定過期時間),這樣比較保險。
2.快取穿透
- 定義: 指快取和資料庫中都沒有的資料,而使用者不斷髮起請求導致快取不命中,這樣請求就繞過快取直接查詢資料庫。此時如果大量請求穿透快取訪問到 DB,就可能導致資料庫壓力過大或者掛掉;這種情況一般很有可能是外部的惡意攻擊;
- 場景: 比如資料庫的 id 都是 1 開始自增上去的,如發起為 id 值為 -1 的資料或 id 為特別大不存在的資料。這時的使用者很可能是攻擊者,攻擊會導致資料庫壓力過大,嚴重會擊垮資料庫。
- 解決:
1)使用 BloomFilter 過濾器,BloomFilter 的特點是存在性檢測,如果 BloomFilter 中不存在,那麼資料一定不存在;如果 BloomFilter 中存在,實際資料也有可能會不存在;
延伸:布隆過濾器:布隆過濾器(Bloom Filter)的核心實現是一個超大的 bitmap 和 K 個雜湊函式。當一個元素被加入集合時,透過K個雜湊函式將這個元素對映成一個位陣列中的K個點,並把它們置為1。檢索時,我們只要看看這些點是不是都是1就(大約)知道集合中有沒有這個資料了:如果這些點有任何一個0,則被檢元素一定不在;如果都是1,則被檢元素很可能在。這就是布隆過濾器的基本思想。 優點:空間效率和查詢效率高,且不會漏判,但是會誤判
2)在介面層增加校驗,比如使用者許可權校驗,引數做校驗,不合法的引數直接程式碼 Return,比如:id 做基礎校驗,id <=0 的直接攔截等
3)從快取取不到的資料,在資料庫中也沒有取到,可以將對應 Key 的 Value 對寫為 null、網路錯誤、稍後重試等值。這樣的值具體取啥看產品,或者具體場景,快取有效時間可以設定短一些如 30 秒(設定太長會導致正常情況無法使用)
4)正常使用者是不會在單秒內發起這麼多次請求的,閘道器層 Nginx 有相關配置項,可以讓運維對單個 IP 每秒訪問次數超出閾值的 IP 都拉黑。
3.快取擊穿
- 定義:指一個 Key 非常熱點,持續高併發情況下集中對這一個熱點資料進行訪問,當這個 Key 在失效的瞬間,持續的高併發就穿破快取,直接請求資料庫,就像在一個完好無損的桶上鑿開了一個洞
- 解決: 1)可以使用互斥鎖,保證同一個程序中針對同一個資料不會併發請求到 DB,減小 DB 壓力。 2)設定熱點資料永遠不過期。
三者之間異同:
三者都是因為快取沒有形成對資料庫的有效保護,而導致請求直接到了資料庫。不同點:快取雪崩是大量快取失效而導致一些列連鎖反應;快取穿透是針對不存在的資料,一般是惡意攻擊;快取擊穿是針對具體的熱點資料在高併發場景下瞬時失效的情況。
以上快取雪崩、快取穿透、快取擊穿如何從 redis 本身的高可靠性的架構中去避免呢?
一般避免以上情況發生我們從三個時間段去分析:
- 事前:Redis 高可用,主從 + 哨兵,Redis cluster,避免全盤崩潰。
- 事中:本地 ehcache 快取 + Hystrix 限流 + 降級,避免 MySQL 被打死。
- 事後:Redis 持久化 RDB+AOF,一旦重啟,自動從磁碟上載入資料,快速恢復快取資料。
tips:限流元件,是指設定每秒透過請求數的限流工具,一般在叢集服務無法再繼續擴容的時候使用。使用後可能導致部分請求不透過,但是隻會影響一部分。
其他
1.快取預熱
定義:快取預熱就是系統上線後,將相關的快取資料直接載入到快取系統。這樣就可以避免在使用者請求的時候,先查詢資料庫,然後再將資料快取的問題。使用者可以直接查詢事先被預熱的快取資料。
2.快取更新
快取更新除了快取伺服器自帶的快取失效策略之外 (Redis 預設的有 6 中策略可供選擇),我們還可以根據具體的業務需求進行自定義的快取淘汰,常見的策略有兩種:
(1) 定時去清理過期的快取;
(2) 當有使用者請求過來時,再判斷這個請求所用到的快取是否過期,過期的話就去底層系統得到新數 據並更新快取。
3.快取降級
當訪問量劇增、服務出現問題 (如響應時間慢或不響應) 或非核心服務影響到核心流程的效能時,即使是有損服務仍然需要保證服務還是可用的。系統可以根據一些關鍵資料進行自動降級,也可以配置開 關實現人工降級。降級的最終目的是保證核心服務可用,即使是有損的。但是核心服務是無法降級的 (如加入購物車、結算等主鏈路上的服務)。走降級!可以返回一些預設的值,或者友情提示,或者空白的值。只要保證資料庫不掛,就能保證系統還可用,就只會影響部分體驗問題。
4.redis 主備切換
主備:即主庫和備庫,備庫只提供備份功能但不提供服務。有的公司可能是主備模式,這就需要 SRE 配合進行主備切換測試,驗證應用服務系統架構設計中是否有主備切換的設計。
5.redis 主從切換
主從:主從都提供服務,主庫進行寫,從庫進行讀。這時候也需要考慮主從切換過程中,Master 是否正常切換為 Slave,且切換過程中寫是否失敗的情況。
更多內容可以學習《測試工程師 Python 工具開發實戰》書籍、《大話效能測試 JMeter 實戰》書籍