這篇文章為大家帶來了關於Redis的相關知識,其中主要介紹了關於快取一致性、快取穿透、快取擊穿、快取雪崩以及快取資料的寫同步的與DB一致性的問題,下面一起來看一下,希望對大家有幫助。
(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函數。使用Google的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快取一致性、快取穿透、快取擊穿及快取雪崩問題的詳細內容。更多資訊請關注PHP中文網其他相關文章!