最全面的快取架構設計(全是乾貨)

2021-08-19 19:05:19 字數 3138 閱讀 6144

程式設計師的日常那些事

2018-05-07 17:53:40

1:快取技術和框架的重要性

網際網路的一些高併發,高效能的專案和系統中,快取技術是起著功不可沒的作用。快取不僅僅是key-value的簡單訪問,它在具體的業務場景中,還是很複雜的,需要很強的架構設計能力。我曾經就遇到過因為快取架構設計不到位,導致了系統崩潰的案例。

2:快取的技術方案分類

1)是做實時性比較高的那塊資料,比如說庫存,銷量之類的這種資料,我們採取的實時的快取+資料庫雙寫的技術方案,雙寫一致性保障的方案。

2)是做實時性要求不高的資料,比如說商品的基本資訊,等等,我們採取的是**快取架構的技術方案,就是說由乙個專門的資料生產的服務,去獲取整個商品詳情頁需要的各種資料,經過處理後,將資料放入各級快取中。

3:高併發以及高可用的複雜系統中的快取架構都有哪些東西

1)在大型的快取架構中,redis是最最基礎的一層。高併發,快取架構中除了redis,還有其他的組成部分,但是redis至關重要。

2)最經典的快取+資料庫讀寫的模式,cache aside pattern。讀的時候,先讀快取,快取沒有的話,那麼就讀資料庫。更新快取分以下兩種方式:

大部分情況下,建議適用刪除更新的方式。其實刪除快取,而不是更新快取,就是乙個lazy計算的思想,不要每次都重新做複雜的計算,不管它會不會用到,而是讓它到需要被使用的時候再重新計算。

舉個例子,乙個快取涉及的表的字段,在1分鐘內就修改了20次,或者是100次,那麼快取跟新20次,100次; 但是這個快取在1分鐘內就被讀取了1次,有大量的冷資料。28**法則,20%的資料,占用了80%的訪問量。實際上,如果你只是刪除快取的話,那麼1分鐘內,這個快取不過就重新計算一次而已,開銷大幅度降低。每次資料過來,就只是刪除快取,然後修改資料庫,如果這個快取,在1分鐘內只是被訪問了1次,那麼只有那1次,快取是要被重新計算的。

3)資料庫與快取雙寫不一致問題的解決方案

問題:併發請求的時候,資料發生了變更,先刪除了快取,然後要去修改資料庫,此時還沒修改。另乙個請求過來,去讀快取,發現快取空了,去查詢資料庫,查到了修改前的舊資料,放到了快取中。

方案:資料庫與快取更新與讀取操作進行非同步序列化。(引入佇列)

更新資料的時候,將相應操作傳送到乙個jvm內部的佇列中。讀取資料的時候,如果發現資料不在快取中,那麼將重新讀取資料的操作也傳送到同乙個jvm內部的佇列中。佇列消費者序列拿到對應的操作,然後一條一條的執行。這樣的話,乙個資料變更的操作,先執行刪除快取,然後再去更新資料庫,但是還沒完成更新。此時如果乙個讀請求過來,讀到了空的快取,那麼可以先將快取更新的請求傳送到佇列中,此時會在佇列中積壓,然後同步等待快取更新完成。

這裡有兩個可以優化的點:

最後,一定要做根據實際業務系統的運**況,去進行一些壓力測試,和模擬線上環境,去看看最繁忙的時候,記憶體佇列可能會擠壓多少更新操作,可能會導致最後乙個更新操作對應的讀請求,會hang多少時間,如果讀請求在200ms返回,如果你計算過後,哪怕是最繁忙的時候,積壓10個更新操作,最多等待200ms,那還可以的。如果乙個記憶體佇列可能積壓的更新操作特別多,那麼你就要加機器,讓每個機器上部署的服務例項處理更少的資料,那麼每個記憶體佇列中積壓的更新操作就會越少。其實根據之前的專案經驗,一般來說資料的寫頻率是很低的,因此實際上正常來說,在佇列中積壓的更新操作應該是很少的。

舉個例子:一秒就100個寫操作。單台機器,20個記憶體佇列,每個記憶體佇列,可能就積壓5個寫操作,每個寫操作效能測試後,一般在20ms左右就完成,那麼針對每個記憶體佇列中的資料的讀請求,也就最多hang一會兒,200ms以內肯定能返回了。如果把寫qps擴大10倍,但是經過剛才的測算,就知道,單機支撐寫qps幾百沒問題,那麼就擴容機器,擴容10倍的機器,10臺機器,每個機器20個佇列,200個佇列。大部分的情況下,應該是這樣的,大量的讀請求過來,都是直接走快取取到資料的,少量情況下,可能遇到讀跟資料更新衝突的情況,如上所述,那麼此時更新操作如果先入佇列,之後可能會瞬間來了對這個資料大量的讀請求,但是因為做了去重的優化,所以也就乙個更新快取的操作跟在它後面。

4)大型快取全量更新問題的解決方案

問題:快取資料很大時,可能導致redis的吞吐量就會急劇下降,網路耗費的資源大。如果不維度化,就導致多個維度的資料混合在乙個快取value中。而且不同維度的資料,可能更新的頻率都大不一樣。拿商品詳情頁來說,如果現在只是將1000個商品的分類批量調整了一下,但是如果商品分類的資料和商品本身的資料混雜在一起。那麼可能導致需要將包括商品在內的大快取value取出來,進行更新,再寫回去,就會很坑爹,耗費大量的資源,redis壓力也很大

方案:快取維度化。舉個例子:商品詳情頁分三個維度:商品維度,商品分類維度,商品店鋪維度。將每個維度的資料都存乙份,比如說商品維度的資料存乙份,商品分類的資料存乙份,商品店鋪的資料存乙份。那麼在不同的維度資料更新的時候,只要去更新對應的維度就可以了。大大減輕了redis的壓力。

6)分布式併發快取重建的衝突問題的解決方案

問題:假如資料在所有的快取中都不存在了(lru演算法弄掉了),就需要重新查詢資料寫入快取。對於分布式的重建快取,在不同的機器上,不同的服務例項中,去做上面的事情,就會出現多個機器分布式重建去讀取相同的資料,然後寫入快取中。

zookeeper分布式鎖的解決併發衝突的方案

7)快取冷啟動的問題的解決方案

問題:新系統第一次上線,此時在快取裡可能是沒有資料的。或者redis快取全盤崩潰了,資料也丟了。導致所有請求打到了mysql。導致mysql直接掛掉。

方案:快取預熱。

8)恐怖的快取雪崩問題的解決方案

問題:快取服務大量的資源全部耗費在訪問redis和源服務無果,最後自己被拖死,無法提供服務。

方案:相對來說,考慮的比較完善的一套方案,分為事前,事中,事後三個層次去思考怎麼來應對快取雪崩的場景。

9)快取穿透問題的解決方案

問題:快取中沒有這樣的資料,資料庫中也沒有這樣的資料。由於快取是不命中時被動寫的,並且出於容錯考慮,如果從儲存層查不到資料則不寫入快取,這將導致這個不存在的資料每次請求都要到儲存層去查詢,失去了快取的意義。在流量大時,可能db就掛掉了,要是有人利用不存在的key頻繁攻擊我們的應用,這就是漏洞。

方案:有很多種方法可以有效地解決快取穿透問題,最常見的則是採用布隆過濾器,將所有可能存在的資料雜湊到乙個足夠大的bitmap中,乙個一定不存在的資料會被 這個bitmap攔截掉,從而避免了對底層儲存系統的查詢壓力。另外也有乙個更為簡單粗暴的方法(我們採用的就是這種),如果乙個查詢返回的資料為空(不管是數 據不存在,還是系統故障),我們仍然把這個空結果進行快取,但它的過期時間會很短,最長不超過五分鐘。

最全面的快取架構設計

網際網路的一些高併發,高效能的專案和系統中,快取技術是起著功不可沒的作用。快取不僅僅是key value的簡單訪問,它在具體的業務場景中,還是很複雜的,需要很強的架構設計能力。我曾經就遇到過因為快取架構設計不到位,導致了系統崩潰的案例。1 是做實時性比較高的那塊資料,比如說庫存,銷量之類的這種資料,...

最全乾貨!軟體架構設計原則

很多人都只聽說過 23 種設計模式,卻不曾知道軟體架構設計原則。無論是想寫出一手優雅的 還是為了更好的重構專案 又或者只是為了讓身邊的同事對你另眼相看,學習軟體架構設計原則都是程式設計師繞不開的話題。如何提高自己的開發效率?提高 的復用性 可擴充套件性 可維護性。如何正確理解設計模式思想?首先理解設...

快取架構設計

快取架構設計 需求分析 快取是一種提高系統讀效能的常見技術,對於讀多寫少的應用場景,我們經常使用快取來進行優化。例如對於使用者的餘額資訊表account uid,money 業務上的需求是 查詢使用者的餘額,select money from account where uid 佔99 的請求 更改...