快取七大經典問題

2022-02-24 20:18:29 字數 3860 閱讀 6569

1. 快取失效

原因分析

在寫快取時,我們一般會根據業務的訪問特點,給每種業務資料預置乙個過期時間,在寫快取時把這個過期時間帶上,讓快取資料在這個固定的過期時間後被淘汰。一般情況下,因為快取資料是逐步寫入的,所以也是逐步過期被淘汰的。但在某些場景,一大批資料會被系統主動或被動從 db 批量載入,然後寫入快取。這些資料寫入快取時,由於使用相同的過期時間,在經歷這個過期時間之後,這批資料就會一起到期,從而被快取淘汰。此時,對這批資料的所有請求,都會出現快取失效,從而都穿透到 db,db 由於查詢量太大,就很容易壓力大增,請求變慢。

解決方法

對於批量 key 快取失效的問題,原因既然是預置的固定過期時間,那解決方案也從這裡入手。設計快取的過期時間時,使用公式:過期時間 = base 時間 + 隨機時間。即相同業務資料寫快取時,在基礎過期時間之上,再加乙個隨機的過期時間,讓資料在未來一段時間內慢慢過期,避免瞬時全部過期,對 db 造成過大壓力。

2. 快取穿透

原因分析

快取穿透存在的原因,就是因為我們在系統設計時,更多考慮的是正常訪問路徑,對特殊訪問路徑、異常訪問路徑考慮相對欠缺。

但是如果使用者訪問的是乙個不存在的 key,查 db 返回空(即乙個 null),那就不會把這個空寫回 cache。那以後不管查詢多少次這個不存在的 key,都會 cache miss,都會查詢 db。整個系統就會退化成乙個 「前端 + db「的系統,由於 db 的吞吐只在 cache 的 1%~2% 以下,如果有特殊訪客,大量訪問這些不存在的 key,就會導致系統的效能嚴重退化,影響正常使用者的訪問。

解決方法

查詢這些不存在的資料時,第一次查 db,雖然沒查到結果返回 null,仍然記錄這個 key 到快取,只是這個 key 對應的 value 是乙個特殊設定的值。但是如果特殊訪客持續訪問大量的不存在的 key,這些 key 即便只存乙個簡單的預設值,也會占用大量的快取空間,導致正常 key 的命中率下降。所以進一步的改進措施是,對這些不存在的 key 只存較短的時間,讓它們盡快過期;或者將這些不存在的 key 存在乙個獨立的公共快取,從快取查詢時,先查正常的快取元件,如果 miss,則查一下公共的非法 key 的快取,如果後者命中,直接返回,否則穿透 db,如果查出來是空,則回種到非法 key 快取,否則回種到正常快取

構建乙個 bloomfilter 快取過濾器,記錄全量資料,這樣訪問資料時,可以直接通過 bloomfilter 判斷這個 key 是否存在,如果不存在直接返回即可,根本無需查快取和 db。但是 bloomfilter 要快取全量的 key,這就要求全量的 key 數量不大,10 億條資料以內最佳,因為 10 億 條資料大概要占用 1.2gb 的記憶體。也可以用 bloomfilter 快取非法 key,每次發現乙個 key 是不存在的非法 key,就記錄到 bloomfilter 中,這種記錄方案,會導致 bloomfilter 儲存的 key 持續高速增長,為了避免記錄 key 太多而導致誤判率增大,需要定期清零處理。

3. 快取雪崩

原因分析

快取雪崩按照快取是否 rehash(即是否漂移)分兩種情況:

快取不支援 rehash 導致的系統雪崩不可用

快取支援 rehash 導致的快取雪崩不可用

在上述兩種情況中,快取不進行 rehash 時產生的雪崩,一般是由於較多快取節點不可用,請求穿透導致 db 也過載不可用,最終整個系統雪崩不可用的。而快取支援 rehash 時產生的雪崩,則大多跟流量洪峰有關,流量洪峰到達,引發部分快取節點過載 crash,然後因 rehash 擴散到其他快取節點,最終整個快取體系異常。

解決方法

對業務 db 的訪問增加讀寫開關,當發現 db 請求變慢、阻塞,慢請求超過閥值時,就會關閉讀開關,部分或所有讀 db 的請求進行 failfast 立即返回,待 db 恢復後再開啟讀開關,如下圖。

對快取增加多個副本,快取異常或請求 miss 後,再讀取其他快取副本,而且多個快取副本盡量部署在不同機架,從而確保在任何情況下,快取系統都會正常對外提供服務

對快取體系進行實時監控,當請求訪問的慢速比超過閥值時,及時報警,通過機器替換、服務替換進行及時恢復;也可以通過各種自動故障轉移策略,自動關閉異常介面、停止邊緣服務、停止部分非核心功能措施,確保在極端場景下,核心功能的正常執行。

4. 資料不一致

原因分析

不一致的問題大多跟快取更新異常有關。比如更新 db 後,寫快取失敗,從而導致快取中存的是老資料。另外,如果系統採用一致性 hash 分布,同時採用 rehash 自動漂移策略,在節點多次上下線之後,也會產生髒資料。快取有多個副本時,更新某個副本失敗,也會導致這個副本的資料是老資料。

5. 資料併發競爭

原因分析

資料併發競爭,主要是由於多個程序 / 執行緒中,有大量併發請求獲取相同的資料,而這個資料 key 因為正好過期、被剔除等各種原因在快取中不存在,這些程序 / 執行緒之間沒有任何協調,然後一起併發查詢 db,請求那個相同的 key,最終導致 db 壓力大增

解決方法

全域性鎖。即當快取請求 miss 後,先嘗試加全域性鎖,只有加全域性鎖成功的執行緒,才可以到 db 去載入資料。其他程序 / 執行緒在讀取快取資料 miss 時,如果發現這個 key 有全域性鎖,就進行等待,待之前的執行緒將資料從 db 回種到快取後,再從快取獲取。

對快取資料保持多個備份,即便其中乙個備份中的資料過期或被剔除了,還可以訪問其他備份,從而減少資料併發競爭的情況。

6.hot key

原因分析

hot key 引發快取系統異常,主要是因為數十萬、數百萬的使用者同時請求同乙個 key,流量集中打在乙個快取節點機器,這個快取機器很容易被打到物理網絡卡、頻寬、cpu 的極限,從而導致快取訪問變慢、卡頓。

解決方案

找到對應的熱點 key,將這些熱 key 進行分散處理,比如乙個熱 key 名字叫 hotkey,可以被分散為 hotkey#1、hotkey#2、hotkey#3,……hotkey#n,這 n 個 key 分散存在多個快取節點,然後 client 端請求時,隨機訪問其中某個字尾的 hotkey,這樣就可以把熱 key 的請求打散,避免乙個快取節點過載

也可以 key 的名字不變,對快取提前進行多副本 + 多級結合的快取架構設計。再次,如果熱 key 較多,還可以通過監控體系對快取的 sla 實時監控,通過快速擴容來減少熱 key 的衝擊。最後,業務端還可以使用本地快取,將這些熱 key 記錄在本地快取,來減少對遠端快取的衝擊

7.big key

原因分析

造成這些大 key 慢查詢的原因很多。如果這些大 key 佔總體資料的比例很小,存 mc,對應的 slab 較少,導致很容易被頻繁剔除,db 反覆載入,從而導致查詢較慢。如果業務中這種大 key 很多,而這種 key 被大量訪問,快取元件的網絡卡、頻寬很容易被打滿,也會導致較多的大 key 慢查詢。另外,如果大 key 快取的字段較多,每個欄位的變更都會引發對這個快取資料的變更,同時這些 key 也會被頻繁地讀取,讀寫相互影響,也會導致慢查現象。最後,大 key 一旦被快取淘汰,db 載入可能需要花費很多時間,這也會導致大 key 查詢慢的問題。

解決方案

如果資料存在 mc 中,可以設計乙個快取閥值,當 value 的長度超過閥值,則對內容啟用壓縮,讓 kv 盡量保持小的 size,其次評估大 key 所佔的比例,在 mc 啟動之初,就立即預寫足夠資料的大 key,讓 mc 預先分配足夠多的 trunk size 較大的 slab。確保後面系統執行時,大 key 有足夠的空間來進行快取。

如果資料存在 redis 中,比如業務資料存 set 格式,大 key 對應的 set 結構有幾千幾萬個元素,這種寫入 redis 時會消耗很長的時間,導致 redis 卡頓。此時,可以擴充套件新的資料結構,同時讓 client 在這些大 key 寫快取之前,進行序列化構建,然後通過 restore 一次性寫入

將大 key 分拆為多個 key,儘量減少大 key 的存在。同時由於大 key 一旦穿透到 db,載入耗時很大,所以可以對這些大 key 進行特殊照顧,比如設定較長的過期時間,比如快取內部在淘汰 key 時,同等條件下,盡量不淘汰這些大 key。

MoreWindows白話經典演算法之七大排序總結篇

在我的部落格對氣泡排序,直接插入排序,直接選擇排序,希爾排序,歸併排序,快速排序和堆排序 有提議到這本 morewindows白話經典演算法之七大排序 電子書講解細緻用來平時學習是非常好的,但是頁數有22頁,不太合適做面試前的複習資料。因此在這裡將這七種常用的排序方法進行下總結,以便大家更好的複習這...

四大經典同步問題

一組生產者程序和一組消費者程序共享乙個初始為空,大小為n的緩衝區,只有緩衝區沒滿時,生產者才能把訊息放入緩衝區,否則必須等待,只有緩衝區不空時,消費者才能從中取出訊息,否則必須等待。由於緩衝區是臨界資源,它只允許乙個生產者放入訊息或乙個消費者從中取出訊息。關係分析 生產者和消費者對緩衝區訪問都是互斥...

PHP安裝十大經典問題

1.unix windows 我的php ini 檔案應該放在哪?unix下預設它應該放在 usr local lib 目錄下。這是它的 lib.很多人會在編譯時改變它,使用 with config file path 標誌。例如,你可以這樣設它 with config file path etc ...