有時候被問到redis 擊穿和雪崩,啥?瞬間一臉懵逼。。。今天看到一篇講解的很透徹、細緻的博文,看了受益良多,特此也分享給各位碼農盆友。
主要講的問題:
在乙個面臨高併發系統中,快取幾乎成了每個架構師應對高流量的首衝解決方案,但是,乙個好的快取系統,除了和資料庫一致性問題之外,還存在著其他問題,給整體的系統設計引入了額外的複雜性。而這些複雜性問題的解決方案也直接了影響系統的穩定性,最常見的比如快取的命中率問題,在乙個高併發系統中,核心功能的快取命中率一般要保持在90%以上甚至更高,如果低於這個命中率,整個系統可能就面臨著隨時被峰值流量擊垮的可能,這個時候我們就需要優化快取的使用方式了。
今天看了下自己本地 redis 的快取命中率,瞬間人社崩塌!
快取 命中率 = keyspace_hits / ( keyspace_hits + keyspace_misses ) ,我的 命中率 = 1 / (1 + 2) = 33.3333...%
聽說你還不會快取?
談了千百遍的快取資料的一致性問題
如果按照傳統的快取和db的流程,乙個請求到來的時候,首先會查詢快取中是否存在,如果快取中不存在則去查詢對應的資料庫。假如系統每秒的請求量為10000,而快取的命中率為60%,則每秒穿透到資料庫的請求數為4000,對於關係型資料庫mysql來說,每秒4000的請求量對於分了一主三從的mysql資料庫架構來說也已經足夠大了,再加上主從的同步延遲等諸多因素,這個時候你的mysql已經行走在down機邊緣了。
快取的最終目的,是在保證請求低延遲的情況下,盡最大努力提高系統的吞吐量首先看下簡單易懂的(重要知識點):
擊穿和雪崩的區別:
與快取穿透現象不同,快取穿透是指快取中不存在資料而造成會對資料庫造成大量查詢,而快取雪崩是因為快取中存在資料,但是同時大量過期造成。但是本質上是一樣的,都是對資料庫造成了大量的請求。
那快取系統可能會影響系統崩潰的原因有那些呢?
快取穿透 是指:當乙個請求到來的時候,在快取中沒有查詢到對應的資料(快取未命中),業務系統不得不從資料庫(這裡其實可以籠統的成為後端系統)中載入資料發生快取穿透的原因根據場景分為兩種:
1、請求的資料在快取和資料中都不存在
當資料在快取和資料庫都不存在的時候,如果按照一般的快取設計,每次請求都會到資料庫查詢一次,然後返回不存在,這種場景下,快取系統幾乎沒有起任何作用。在正常的業務系統中,發生這種情況的概率比較小,就算偶爾發生,也不會對資料庫造成根本上的壓力。
最可怕的是出現一些異常情況,比如系統中有死迴圈的查詢或者被黑客攻擊的時候,尤其是後者,他會故意偽造大量的請求來讀取不存在的資料而造成資料庫的down機,最典型的場景為:如果系統的使用者id是連續遞增的int型,黑客很容易偽造使用者id來模擬大量的請求。
2、請求的資料在快取中不存在,在資料庫中存在
這種場景一般屬於業務的正常需求,因為快取系統的容量一般是有限制的,比如我們最常用的redis做為快取,就受到伺服器記憶體大小的限制,所以所有的業務資料不可能都放入快取系統中,根據網際網路資料的二八規則,我們可以優先把訪問最頻繁的熱點資料放入快取系統,這樣就能利用快取的優勢來抗住主要的流量**,而剩餘的非熱點資料,就算是有穿透資料庫的可能性,也不會對資料庫造成致命壓力。
換句話說,每個系統發生快取穿透是不可避免的,而我們需要做的是盡量避免大量的請求發生穿透,那怎麼解決快取穿透問題呢?解決快取的穿透問題本質上是要解決怎麼樣攔截請求的問題,一般情況下會有以下幾種方案:
a、回寫空值
當請求的資料在資料庫中不存在的時候,快取系統可以把對應的key寫入乙個空值,這樣當下次同樣的請求就不會直接穿透資料庫,而直接返回快取中的空值了。這種方案是最簡單粗暴的,但是要注意幾點:
b、布隆過濾器//獲取使用者資訊
public static userinfo getuserinfo(int userid)
return userinfo;
}
布隆過濾器:將所有可能存在的資料雜湊到乙個足夠大的 bitmap 中,乙個一定不存在的資料會被這個bitmap攔截掉,從而避免了對底層儲存系統的查詢壓力布隆過濾器有幾個很大的優勢優雅快速的統計千萬級別uv
由於布隆過濾器基於hash演算法,所以在時間複雜度上是o(1),在應對高併發的場景下非常合適,不過使用布隆過濾器要求系統在產生資料的時候需要在布隆過濾器同時也寫入資料,而且布隆過濾器也不支援刪除資料,因為多個資料可能會重用同乙個位置。
快取雪崩是指快取中資料大批量同時過期,造成查詢資料庫資料量巨大,引起資料庫壓力過大導致系統崩潰。擊穿和雪崩的區別:與快取穿透現象不同,快取穿透是指快取中不存在資料而造成會對資料庫造成大量查詢,而快取雪崩是因為快取中存在資料,但是同時大量過期造成。但是本質上是一樣的,都是對資料庫造成了大量的請求。
無論是穿透還是雪崩都面臨著同樣的資料會有多個執行緒同時請求,同時查詢資料庫,同時回寫快取的一致性問題。舉例來說,當多個執行緒同時請求使用者id為1的使用者,這個時候快取正好失效,那這多個執行緒同時會查詢資料庫,然後同時會回寫快取,最可怕的是,這個回寫的過程中,另外乙個執行緒更新了資料庫,就造成了資料不一致,這個問題在之前的文章中著重講過,大家一定要注意。
同樣的資料會被多個執行緒產生多個請求是產生雪崩的乙個原因,針對這種情況的解決方案是把多個執行緒的請求順序化,使其只有乙個執行緒會產生對資料庫的查詢操作,比如最常見的鎖機制(分布式鎖機制),現在最常見的分布式鎖是用redis來實現,但是redis實現分布式鎖也有一定的坑,可以參見之前的文章(如果使用的是actor模型的話會在無鎖的模式下更優雅的實現請求順序化)
redis做分布式鎖可能不那麼簡單
多個快取key同時失效的場景是產生雪崩的主要原因,針對這樣的場景一般可以利用以下幾種方案來解決
1、設定不同過期時間
給快取的每個key設定不同的過期時間是最簡單的防止快取雪崩的手段,整體思路是給每個快取的key在系統設定的過期時間之上加乙個隨機值,或者乾脆是直接隨機乙個值,有效的平衡key批量過期時間段,消掉單位之間內過期key數量的峰值。
2、後台單獨執行緒更新public static int setuserinfo(int userid)
return 0;
}
這種場景下,可以把快取設定為永不過期,快取的更新不是由業務執行緒來更新,而是由專門的執行緒去負責。當快取的key有更新時候,業務方向mq傳送乙個訊息,更新快取的執行緒會監聽這個mq來實時響應以便更新快取中對應的資料。不過這種方式要考慮到快取淘汰的場景,當乙個快取的key被淘汰之後,其實也可以向mq傳送乙個訊息,以達到更新執行緒重新回寫key的操作。
和資料庫一樣,快取系統的設計同樣需要考慮高可用和擴充套件性。雖然快取系統本身的效能已經比較高了,但是對於一些特殊的高併發的熱點資料,還是會遇到單機的瓶頸。舉個栗子:假如某個明星出軌了,這個資訊資料會快取在某個快取伺服器的節點上,大量的請求會到達這個伺服器節點,當到達一定程度的時候同樣會發生down機的情況。類似於資料庫的主從架構,快取系統也可以複製多分快取副本到其他伺服器上,這樣就可以將應用的請求分散到多個快取伺服器上,緩解由於熱點資料出現的單點問題。
和資料庫主從一樣,快取的多個副本也面臨著資料的一致性問題,同步延遲問題,還有主從伺服器相同key的過期時間問題。
至於快取系統的擴充套件性同樣的道理,也可以利用「分片」的原則,利用一致性雜湊演算法將不同的請求路由到不同的快取伺服器節點,來達到水平擴充套件的要求,這一點和應用的水平擴充套件道理一樣。
Redis穿透 擊穿和雪崩
概念 key對應的資料在資料來源並不存在,每次針對此key的請求從快取獲取不到,請求都會到資料來源,從而可能壓垮資料來源。比如用乙個不存在的使用者id獲取使用者資訊,不論快取還是資料庫都沒有,若黑客利用此漏洞進行攻擊可能壓垮資料庫。解決方案 乙個一定不存在快取及查詢不到的資料,由於快取是不命中時被動...
redis 雪崩,穿透,擊穿
雪崩 同一時間key大面積失效 多出現在定時任務重新整理時 處理方案 1,把每個key的失效時間都加乙個隨機值 2,設定熱點資料永不過期,有更新操作就更新快取 3,如果時集群,將熱點資料均勻分布在不同的redis庫仲 穿透 快取和資料庫仲都沒有的資料時,使用者不斷的發起請求 處理方案 1,在介面層增...
redis擊穿與雪崩
快取穿透的概念很簡單,使用者想要查詢乙個資料,發現redis記憶體資料庫沒有,也就是快取沒有命中,於是向持久層資料庫查詢。發現也沒有,於是本次查詢失敗。當使用者很多的時候,快取都沒有命中,於是都去請求了持久層資料庫。這會給持久層資料庫造成很大的壓力,這時候就相當於出現了快取穿透。這裡需要注意和快取擊...