Redis和數據庫一致性


 

1、實時同步

     對強一致要求比較高的,應采用實時同步方案,即查詢緩存查詢不到再從DB查詢,保存到緩存;

     更新緩存時,先更新數據庫,再將緩存的設置過期(建議不要去更新緩存內容,直接設置緩存過期)。

 

   為什么不去更新緩存內容,而是設置緩存過期呢?

   答:我們先來了解兩個概念

 

1.1. 緩存穿透

      緩存穿透是指查詢一個數據庫中一定不存在的數據,由於緩存是不命中時需要從數據庫中查詢,查不到數據則不寫入緩存,這就將導致這個不存在的數據每次請求都要到數據庫中查詢,造成緩存穿透。

        你可以通俗的理解,直接把緩存穿透了,沒有利用到緩存。。。

 

        舉例:

         If(redis存在此key){

             查詢redis

}else{

  查詢數據庫,如果能查到數據,就寫入到redis

}

         這一段代碼邏輯就會造成緩存穿透的惡果。。

         你想,假設這個查詢數據庫中永遠沒有值,那么這個redis中這個key是不是就不會創建,那么代碼就永遠只走查詢數據庫這段代碼,跟redis沒關系了。。

 

         解決緩存穿透很簡單,就是數據庫查詢不到也要緩存結果,不過緩存結果賦值為空而已,但它的過期時間會很短,最長不超過五分鍾。

         If(redis存在此key){

             Var redisValue=查詢redis

返回值

}else{

  查詢數據庫.

If(能查到數據){

就寫入到redis

}else{

    如果查詢不到,也寫到redis中,只不過值是空值

}

}

 

要注意的點:

 第一,空值做了緩存,意味着緩存層中存了更多的鍵,需要更多的內存空間 ( 如果是攻擊,問題更嚴重 ),比較有效的方法是針對這類數據設置一個較短的過期時間,讓其自動剔除。

 

第二,緩存層和存儲層的數據會有一段時間窗口的不一致,可能會對業務有一定影響。例如過期時間設置為 5分鍾,如果此時存儲層添加了這個數據,那此段時間就會出現緩存層和存儲層數據的不一致,此時可以利用消息系統或者其他方式清除掉緩存層中的空對象。

第三,Insert時需要事先移除要查詢的key,否則即便DB中有值也查詢不到(當然也可以設置空緩存的過期時間)

1.2. 緩存雪崩

     如果緩存集中在一段時間內失效,發生大量的緩存穿透,所有的查詢都落在數據庫上,造成了緩存雪崩。

      這個沒有完美解決辦法,但可以分析用戶行為,盡量讓失效時間點均勻分布。大多數系統設計者考慮用加鎖或者隊列的方式保證緩存的單線程(進程)寫,從而避免失效時大量的並發請求落到底層存儲系統上。

 

    解決方法

 

   1. 加鎖排隊. 限流-- 限流算法. 1.計數 2.滑動窗口 3.  令牌桶Token Bucket 4.漏桶 leaky bucket [1]

 

 在緩存失效后,通過加鎖或者隊列來控制讀數據庫寫緩存的線程數量。比如對某個key只允許一個線程查詢數據和寫緩存,其他線程等待。

 

 業界比較常用的做法,是使用mutex。簡單地來說,就是在緩存失效的時候(判斷拿出來的值為空),不是立即去load db,而是先使用緩存工具的某些帶成功操作返回值的操作(比如RedisSETNX或者MemcacheADD)去set一個mutex key,當操作返回成功時,再進行load db的操作並回設緩存;否則,就重試整個get緩存的方法。

 

SETNX,是「SET if Not eXists」的縮寫,也就是只有不存在的時候才設置,可以利用它來實現鎖的效果。

 

     2.數據預熱

 

 

 

  可以通過緩存reload機制,預先去更新緩存,再即將發生大並發訪問前手動觸發加載緩存不同的key,設置不同的過期時間,讓緩存失效的時間點盡量均勻

 

     3.做二級緩存,或者雙緩存策略。

 

     A1為原始緩存,A2為拷貝緩存,A1失效時,可以訪問A2A1緩存失效時間設置為短期,A2設置為長期。

 

      4.緩存永遠不過期

 

 這里的“永遠不過期”包含兩層意思:

 

    (1) 從緩存上看,確實沒有設置過期時間,這就保證了,不會出現熱點key過期問題,也就是“物理”不過期。

 

     (2) 從功能上看,如果不過期,那不就成靜態的了嗎?所以我們把過期時間存在key對應的value里,如果發現要過期了,通過一個后台的異步線程進行緩存的構建,也就是“邏輯”過期.

 

 從實戰看,這種方法對於性能非常友好,唯一不足的就是構建緩存時候,其余線程(非構建緩存的線程)可能訪問的是老數據,但是對於一般的互聯網功能來說這個還是可以忍受。

 

 

 

 

1.3. 熱點Key

     熱點key:某個key訪問非常頻繁,當key失效的時候有大量的線程來構建緩存,導致負載增加,系統崩潰。

        解決辦法:

1、使用鎖,單機用synchronizedlock等,分布式用分布式鎖

2、緩存過期時間不設置,而是設置在key對應的value里。如果檢測到存的時間超過過期時間則異步更新緩存

3、value設置一個比過期時間t0小的過期時間值t1,當t1過期的時候,延長t1並做更新緩存操作

4、設置標簽緩存,標簽緩存設置過期時間,標簽緩存過期后,需要異步更新實際緩存

 

2、異步隊列

   對於並發程度較高的,可采用異步隊列的方式同步,可采用kafka等消息中間件處理消息生產和消費。

 

 

 

3、使用阿里的同步工具canal

 

canal是阿里巴巴旗下的一款開源項目,純Java開發。基於數據庫增量日志解析,提供增量數據訂閱&消費,目前主要支持了MySQL(也支持mariaDB)。

 

起源:早期,阿里巴巴B2B公司因為存在杭州和美國雙機房部署,存在跨機房同步的業務需求。不過早期的數據庫同步業務,主要是基於trigger的方式獲取增量變更,不過從2010年開始,阿里系公司開始逐步的嘗試基於數據庫的日志解析,獲取增量變更進行同步,由此衍生出了增量訂閱&消費的業務,從此開啟了一段新紀元。

 

基於日志增量訂閱&消費支持的業務:

 

數據庫鏡像

數據庫實時備份

多級索引 (賣家和買家各自分庫索引)

search build

業務cache刷新

價格變化等重要業務消息

 

簡單來說,Canal 會將自己偽裝成 MySQL 從節點(Slave),並從主節點(Master)獲取 Binlog,解析和貯存后供下游消費端使用。Canal 包含兩個組成部分:服務端和客戶端。服務端負責連接至不同的 MySQL 實例,並為每個實例維護一個事件消息隊列;客戶端則可以訂閱這些隊列中的數據變更事件,處理並存儲到數據倉庫中

 

 

原理相對比較簡單:

  1. canal模擬mysql slave的交互協議,偽裝自己為mysql slave,向mysql master發送dump協議
  2. mysql master收到dump請求,開始推送binary logslave(也就是canal)
  3. canal解析binary log對象(原始為byte)

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM