實現商鋪和緩存與數據庫雙寫一致(以及強雙寫一致策略)
redis點評項目采用的是延時雙刪策略
雙刪:
我們更新完數據庫之后刪除緩存,這樣即使有線程并發進來查詢,會發現緩存中沒有數據,從而會去mysql中查找最新的數據。
延時:
在高并發的情況下,如果一個請求更新了數據庫,另一個請求在數據庫更新完成后但緩存刪除之前讀取了舊數據并存入緩存,可能會導致短暫的數據不一致。
延時的作用:
添加過期時間保證緩存會定期失效,觸發重新從數據庫加載最新數據,從而減少臟數據問題。但是它也只能降低臟數據的風險,不能保證數據的強一致性。
在項目中具體操作是:
根據id查詢店鋪時,如果緩存未命中,則查詢數據庫,將數據庫結果寫入緩存,并設置超時時間。
根據id修改店鋪時,先修改數據庫,再刪除緩存。
除了延時其他保證數據強一致性的方案:
方案一:
給查詢店鋪,修改店鋪這兩步加分布式鎖。
方案二:
可以采用加讀寫鎖(分布式鎖的優化方案)
讀鎖(共享鎖):允許多個線程同時獲取(進行讀操作)
寫鎖(排他鎖):一次只允許一個線程獲取(進行寫操作)
簡而言之就是在多線程同時讀數據庫或者緩存數據時,是可以并發處理的,不會阻塞,還可以提高系統吞吐量(分布式鎖在一個線程讀數據庫時其他線程會被阻塞);但是!當一個寫成修改數據庫數據,這時它獲取了寫鎖,它成功實現修改數據庫信息然后更新緩存。在這個過程中系統會保證其他線程不能獲取讀鎖或寫鎖,不能進行任何操作,從而保證數據庫緩存雙寫一致性。
方案三:
可以采用消息隊列
當目標要修改數據時,由service發布信息到消息對列中,然后緩存的service監聽信息隊列的信息,得到數據庫信息要被修改的信息執行更新緩存操作。(缺點是具有一定延時性)
緩存數據庫其中一個宕機了怎么辦?
對于延時雙刪策略來說:
redis出現宕機 | 緩存完全不可用 | 數據請求比較小 | 直接將請求暫時交給數據庫處理 |
數據請求比較大 | 方案一:熔斷和降級:返回靜態默認值或緩存過的最后已知值 方案二:啟用本地緩存(Caffeine)作為二級緩存 方案三:對數據庫請求限流,避免雪崩 | ||
緩存部分節點宕機 | 方案一:如果是Redis集群,利用集群自動切換,故障轉移的能力。 方案二:數據分片和數據冗余:數據分片將數據分布在不同的服務器節點上,數據冗余會在多個節點上存儲數據副本。所以即使某節點宕機還是可以獲得部分數據。 | ||
數據庫出現宕機 | 主庫宕機 | 方案一:進行主從切換:可以提前設置一個備用庫,當主庫宕機就自動切換為備用庫。 方案二:寫入消息隊列中(Kafka),等數據庫恢復后從隊列中獲取數據。 | |
從庫宕機 | 負載均衡:將請求分發到其他從庫。 |
如果采用消息隊列:
redis宕機:因為消息隊列中可能還存在未被消費的訂單消息,所以等訂單信息消費之后,將后續的訂單請求直接由mysql數據庫完成,保證數據的一致性。
Kafka宕機:先將redis中未寫入數據庫的訂單信息寫入數據庫中。消息隊列的本質作用是異步削峰,假如消息隊列宕機,后續新的請求只能直接操作數據庫而不是發送給消息隊列,這樣可能會造成數據庫較大壓力,這里可以做一些熔斷和降級的處理邏輯。
?