相關推薦:《》
(1)快取失效一致性問題
一般快取的使用方式是:先讀取快取,若不存在則從DB中讀取,並將結果寫入到快取中;下次資料讀取時便可以直接從快取中獲取資料。【相關推薦:Redis視訊教學】
資料的修改是直接失效快取資料,再修改DB內容,避免DB修改成功,但由於網路或者其他問題導致快取資料沒有清理,造成了髒資料。但這樣仍然無法避免髒資料的產生,一種並行的場景下:假設業務對資料Key:Hello Value:World有大量的讀取和修改請求。執行緒A向OCS讀取Key:Hello,得到Not Found結果,開始向DB請求資料,得到資料Key:Hello Value:World;接下來準備向OCS寫入此條資料,但在寫入OCS前(網路,CPU都等可能導致A執行緒處理速度降低)另一B執行緒請求修改資料Key:Hello Value:OCS,首先執行失效快取動作(因為B執行緒並不知道是否有此條資料,因此直接執行失效操作),OCS成功處理了失效請求。轉回到A執行緒繼續執行寫入OCS,將Key:Hello Value:World寫入到快取中,A執行緒任務結束;B執行緒也成功修改了DB資料內容為Key:Hello Value:OCS。為了解決這個問題,OCS擴充了Memcached協定(公有云即將支援),增加了deleteAndIncVersion介面。此介面並不會真的刪除資料,而是給資料打了標籤,表明已失效狀態,並且增加資料版本號;如果資料不存在則寫入NULL,同時也生成亂資料版本號。OCS寫入支援原子對比版本號:假設傳入的版本號與OCS儲存的資料版本號一致或者原資料不存在,則准許寫入,否則拒絕修改。
回到剛才的場景上:執行緒A向OCS讀取Key:Hello,得到Not Found結果,開始向DB請求資料,得到資料Key:Hello Value:World;接下來準備向OCS寫入此條資料,版本號資訊預設為1;在A寫入OCS前另一個B執行緒發起了動作修改資料Key:Hello Value:OCS,首先執行刪除快取動作,OCS順利處理了deleteAndIncVersion請求,生成了隨機版本號12345(約定大於1000)。轉回到A執行緒繼續執行寫入OCS,請求將Key:Hello Value:World寫入,此時快取系統發現傳入的版本號資訊不匹配(1 != 12345),寫入失敗,A執行緒任務結束;B執行緒也成功修改了DB資料內容為Key:Hello Value:OCS。
此時OCS中的資料為Key:Hello Value:NULL Version:12345;DB中的資料為Key:Hello Value:OCS,後續讀任務時會再次嘗試將DB中的資料寫入到OCS中。
(2)快取資料的寫同步的與DB一致性問題
隨著網站規模增長和可靠性的提升,會面臨多IDC的部署,每個IDC都有一套獨立的DB和快取系統,這時快取一致性又成了突出的問題。
首先快取系統為了保證高效率,會杜絕磁碟IO,哪怕是寫BINLOG;當然快取系統為了效能可以只同步刪除,不同步寫入,那麼快取的同步一般會優先於DB同步到達(畢竟快取系統的效率要高得多),那麼就會出現快取中無資料,DB中是舊資料的場景。此時,有業務請求資料,讀取快取Not Found,從DB讀取並載入到快取中的仍然是舊資料,DB資料同步到達時也只更新了DB,快取髒資料無法被清除。
從上面的情況可以看出,不一致的根本原因是異構系統之間無法協同同步,不能保證DB資料先同步,快取資料後同步。所以就要考慮快取系統如何等待DB同步,或者能否做到兩者共用一套同步機制?快取同步也依賴DB BINLOG是一個可行的方案。
IDC1中的DB,通過BINLOG同步給IDC2中的DB,此事IDC2-DB資料修改也會產生自身的BINLOG,快取的資料同步就可以通過IDC2-DB BINLOG進行。快取同步模組分析BINLOG後,失效相應的快取Key,同步從並行改為序列,保證了先後順序。
(3)快取穿透(DB承受了沒有必要的查詢流量)
方法一:是布隆過濾器。它是一種空間效率極高的概率型演演算法和資料結構,用於判斷一個元素是否在集合中(類似Hashset)。它的核心是一個很長的二進位制向量和一系列的hash函數。使用谷歌的guava實現布隆過濾器。1)存在誤算率,隨著存入的元素數量增加,誤算率也隨著增加2)一般情況下不能從布隆過濾器刪除元素3)陣列長度以及hash函數個數確定過程複雜,布隆過濾器的使用場景?1)垃圾郵件地址過濾(地址數量很龐大)2)爬蟲URL地址去重3)解決快取擊穿問題
方法二:儲存空結果,並設定空結果的時間
(4)快取雪崩(快取設定同一過期時間,引起的DB洪峰)
方法一:大多數系統設計者考慮用加鎖或者佇列的方式保證快取的單線 程(程序)寫,從而避免失效時大量的並行請求落到底層儲存系統上
方法二:失效時間隨機值
(5)快取擊穿(熱點Key,大量並行讀請求引起的小雪崩)
快取在某個時間點過期的時候,恰好在這個時間點對這個Key有大量的並行請求過來,這些請求發現快取過期一般都會從後端DB載入資料並回設到快取,這個時候大並行的請求可能會瞬間把後端DB壓垮
方法一:1.使用分佈是快取支援的互斥鎖(mutex key),去set一個mutex key,當操作返回成功時,再進行load db的操作並回設快取,也就是load DB 只會一個執行緒處理。
方法二:提前"使用互斥鎖(mutex key):在value內部設定1個超時值(timeout1), timeout1比實際的memcache timeout(timeout2)小。當從cache讀取到timeout1發現它已經過期時候,馬上延長timeout1並重新設定到cache。然後再從資料庫載入資料並設定到cache中。增加了業務程式碼的侵入過多,以及增加了編碼複雜性
方法三: "永遠不過期": 從redis上看,確實沒有設定過期時間,這就保證了,不會出現熱點key過期問題,也就是「物理」不過期。從功能上看,如果不過期,那不就成靜態的了嗎?所以我們把過期時間存在key對應的value裡,如果發現要過期了,通過一個後臺的非同步執行緒進行快取的構建,也就是「邏輯」過期
(6)快取系統常見的快取滿了和資料丟失問題
需要根據具體業務分析,通常我們採用LRU策略處理溢位,Redis的RDB和AOF持久化策略來保證一定情況下的資料安全。
更多程式設計相關知識,請存取:!!
以上就是一起分析Redis快取一致性、快取穿透、快取擊穿及快取雪崩問題的詳細內容,更多請關注TW511.COM其它相關文章!