1. 程式人生 > >redis系列之資料庫與快取資料一致性解決方案

redis系列之資料庫與快取資料一致性解決方案

資料庫與快取讀寫模式策略

寫完資料庫後是否需要馬上更新快取還是直接刪除快取? (1)、如果寫資料庫的值與更新到快取值是一樣的,不需要經過任何的計算,可以馬上更新快取,但是如果對於那種寫資料頻繁而讀資料少的場景並不合適這種解決方案,因為也許還沒有查詢就被刪除或修改了,這樣會浪費時間和資源 (2)、如果寫資料庫的值與更新快取的值不一致,寫入快取中的資料需要經過幾個表的關聯計算後得到的結果插入快取中,那就沒有必要馬上更新快取,只有刪除快取即可,等到查詢的時候在去把計算後得到的結果插入到快取中即可。 所以一般的策略是當更新資料時,先刪除快取資料,然後更新資料庫,而不是更新快取,等要查詢的時候才把最新的資料更新到快取

資料庫與快取雙寫情況下導致資料不一致問題

場景一

當更新資料時,如更新某商品的庫存,當前商品的庫存是100,現在要更新為99,先更新資料庫更改成99,然後刪除快取,發現刪除快取失敗了,這意味著資料庫存的是99,而快取是100,這導致資料庫和快取不一致。

場景一解決方案

這種情況應該是先刪除快取,然後在更新資料庫,如果刪除快取失敗,那就不要更新資料庫,如果說刪除快取成功,而更新資料庫失敗,那查詢的時候只是從資料庫裡查了舊的資料而已,這樣就能保持資料庫與快取的一致性。

場景二

在高併發的情況下,如果當刪除完快取的時候,這時去更新資料庫,但還沒有更新完,另外一個請求來查詢資料,發現快取裡沒有,就去資料庫裡查,還是以上面商品庫存為例,如果資料庫中產品的庫存是100,那麼查詢到的庫存是100,然後插入快取,插入完快取後,原來那個更新資料庫的執行緒把資料庫更新為了99,導致資料庫與快取不一致的情況

場景二解決方案

遇到這種情況,可以用佇列的去解決這個問,建立幾個佇列,如20個,根據商品的ID去做hash值,然後對佇列個數取摸,當有資料更新請求時,先把它丟到佇列裡去,當更新完後在從佇列裡去除,如果在更新的過程中,遇到以上場景,先去快取裡看下有沒有資料,如果沒有,可以先去佇列裡看是否有相同商品ID在做更新,如果有也把查詢的請求傳送到佇列裡去,然後同步等待快取更新完成。
這裡有一個優化點,如果發現佇列裡有一個查詢請求了,那麼就不要放新的查詢操作進去了,用一個while(true)迴圈去查詢快取,迴圈個200MS左右,如果快取裡還沒有則直接取資料庫的舊資料,一般情況下是可以取到的。
在高併發下解決場景二要注意的問題
(1)讀請求時長阻塞
 由於讀請求進行了非常輕度的非同步化,所以一定要注意讀超時的問題,每個讀請求必須在超時間內返回,該解決方案最大的風險在於可能資料更新很頻繁,導致佇列中擠壓了大量的更新操作在裡面,然後讀請求會發生大量的超時,最後導致大量的請求直接走資料庫,像遇到這種情況,一般要做好足夠的壓力測試,如果壓力過大,需要根據實際情況新增機器。
(2)請求併發量過高
 這裡還是要做好壓力測試,多模擬真實場景,併發量在最高的時候QPS多少,扛不住就要多加機器,還有就是做好讀寫比例是多少
(3)多服務例項部署的請求路由
可能這個服務部署了多個例項,那麼必須保證說,執行資料更新操作,以及執行快取更新操作的請求,都通過nginx伺服器路由到相同的服務例項上
(4)熱點商品的路由問題,導致請求的傾斜
某些商品的讀請求特別高,全部打到了相同的機器的相同丟列裡了,可能造成某臺伺服器壓力過大,因為只有在商品資料更新的時候才會清空快取,然後才會導致讀寫併發,所以更新頻率不是太高的話,這個問題的影響並不是很大,但是確實有可能某些伺服器的負載會高一些。

資料庫與快取資料一致性解決方案流程圖


資料庫與快取資料一致性解決方案對應程式碼

商品庫存實體

  1. package com.shux.inventory.entity;  
  2. /** 
  3.  ********************************************** 
  4.  *  描述: 
  5.  * Simba.Hua 
  6.  * 2017年8月30日 
  7.  ********************************************** 
  8. **/
  9. publicclass InventoryProduct {  
  10.     private Integer productId;  
  11.     private Long InventoryCnt;  
  12.     public Integer getProductId() {  
  13.         return productId;  
  14.     }  
  15.     publicvoid setProductId(Integer productId) {  
  16.         this.productId = productId;  
  17.     }  
  18.     public Long getInventoryCnt() {  
  19.         return InventoryCnt;  
  20.     }  
  21.     publicvoid setInventoryCnt(Long inventoryCnt) {  
  22.         InventoryCnt = inventoryCnt;  
  23.     }  
  24. }  

請求介面

  1. /** 
  2.  ********************************************** 
  3.  *  描述: 
  4.  * Simba.Hua 
  5.  * 2017年8月27日 
  6.  ********************************************** 
  7. **/
  8. publicinterface Request {  
  9.     publicvoid process();  
  10.     public Integer getProductId();  
  11.     publicboolean isForceFefresh();  
  12. }  

資料更新請求

  1. package com.shux.inventory.request;  
  2. import org.springframework.transaction.annotation.Transactional;  
  3. import com.shux.inventory.biz.InventoryProductBiz;  
  4. import com.shux.inventory.entity.InventoryProduct;  
  5. /** 
  6.  ********************************************** 
  7.  *  描述:更新庫存資訊 
  8.  *  1、先刪除快取中的資料 
  9.  *  2、更新資料庫中的資料 
  10.  * Simba.Hua 
  11.  * 2017年8月30日 
  12.  ********************************************** 
  13. **/
  14. publicclass InventoryUpdateDBRequest implements Request{  
  15.     private InventoryProductBiz inventoryProductBiz;  
  16.     private InventoryProduct inventoryProduct;  
  17.     public InventoryUpdateDBRequest(InventoryProduct inventoryProduct,InventoryProductBiz inventoryProductBiz){  
  18.         this.inventoryProduct = inventoryProduct;  
  19.         this.inventoryProductBiz = inventoryProductBiz;  
  20.     }  
  21.     @Override
  22.     @Transactional
  23.     publicvoid process() {  
  24.         inventoryProductBiz.removeInventoryProductCache(inventoryProduct.getProductId());  
  25.         inventoryProductBiz.updateInventoryProduct(inventoryProduct);  
  26.     }  
  27.     @Override
  28.     public Integer getProductId() {  
  29.         // TODO Auto-generated method stub
  30.         return inventoryProduct.getProductId();  
  31.     }  
  32.     @Override
  33.     publicboolean isForceFefresh() {  
  34.         // TODO Auto-generated method stub
  35.         returnfalse;  
  36.     }  
  37. }  

查詢請求

  1. package com.shux.inventory.request;  
  2. import com.shux.inventory.biz.InventoryProductBiz;  
  3. import com.shux.inventory.entity.InventoryProduct;  
  4. /** 
  5.  ********************************************** 
  6.  *  描述:查詢快取資料 
  7.  *  1、從資料庫中查詢 
  8.  *  2、從資料庫中查詢後插入到快取中 
  9.  * Simba.Hua 
  10.  * 2017年8月30日 
  11.  ********************************************** 
  12. **/
  13. 相關推薦

    redis系列資料庫快取資料一致性解決方案

    資料庫與快取讀寫模式策略 寫完資料庫後是否需要馬上更新快取還是直接刪除快取? (1)、如果寫資料庫的值與更新到快取值是一樣的,不需要經過任何的計算,可以馬上更新快取,但是如果對於那種寫資料頻繁而讀資料少的場景並不合適這種解決方案,因為也許還沒有查詢就被刪除

    不懂這些高併發分散式架構、分散式系統的資料一致性解決方案,你如何能找到高新網際網路工作呢?強勢解析eBay BASE模式、去哪兒及蘑菇街分散式架構

    網際網路行業是大勢所趨,從招聘工資水平即可看出,那麼如何提升自我技能,滿足網際網路行業技能要求?需要以目標為導向,進行技能提升,本文主要針對高併發分散式系統設計、架構(資料一致性)做了分析,祝各位早日走上屬於自己的"成金之路"。 目錄:問題分析概念解讀Most Simple原理解讀eBey、去哪兒、蘑菇街分

    RDS 5.7三節點企業版時代的資料一致性解決方案

      上篇我們看到了在MySQL主備模式下,我們在資料一致性上做了不少事情,但解決方案都有一定的侷限性,適合部分場景或者解決不徹底的

    MySQL主備模式的資料一致性解決方案

      根據阿里交易型業務的特點,以及在雙十一這樣業內罕有的需求推動下,我們在官方的MySQL基礎上增加了非常多實用的功能、效能補丁。

    分散式事務瞭解嗎?你們的多個服務間資料一致性解決方案是什麼?

    ## 前言 看標題就知道,這個又是個在面試中被問到的問題。這個問題其實是在我上次換工作的時候面試被問到過幾次,之前也沒在意過,覺得這個東西可能比較深奧,我直接說不理解吧。但是隨著Java開發這個行業越來越卷,這次換工作一定要做好充足的準備。把之前落下的坑都填好,再出去受虐(面試)。 ## 什麼是分散式事務 我

    redis快取資料資料庫資料一致性方案

    方式1:資料庫儲存資料,redis不persist redis啟動後,從資料庫載入資料 不要求強一致實時性的讀請求,都由redis處理 要求強一致實時性的讀請求,由資料庫處理 寫請求有2種處理方式,由資料庫處理 - 應用先寫道資料庫,然後更新redis - 應用先寫道資料庫

    redis快取資料資料庫資料一致性方案(好)

    方式1:資料庫儲存資料,redis不persistredis啟動後,從資料庫載入資料不要求強一致實時性的讀請求,都由redis處理要求強一致實時性的讀請求,由資料庫處理寫請求有2種處理方式,由資料庫處理- 應用先寫道資料庫,然後更新redis- 應用先寫道資料庫,然後其它da

    MySQL資料庫Redis快取資料一致性問題

    我們時常接受失望,這樣我們才能不斷重整旗鼓。——《島上書店》 1、引言 在最近專案開發中,採用mysql儲存持久化資料,redis快取熱門資料,遇到一個問題,對於一些強實時性業務,我們需要先將資料寫到mysql上,mysql寫入成功後,再去更新r

    redis系列數據庫緩存數據一致性解決方案

    查詢緩存 src dev 信息 一次 ram ren red getc redis系列之數據庫與緩存數據一致性解決方案 數據庫與緩存讀寫模式策略 寫完數據庫後是否需要馬上更新緩存還是直接刪除緩存? (1)、如果寫數據庫的值與更新到緩存值是一樣的,不需要經過任何的計算,可以馬

    Redis快取維護方案-考慮資料庫快取雙寫、redis和本地資料庫事務一致性資料庫主從同步延遲的情況怎麼解決快取資料庫不一致

    一般常用的快取方案有兩種: 第一種 讀的時候,先讀快取,快取沒有的話,讀資料庫,取出資料後放入快取,同時返回響應。 更新的時候,

    一、redis系列基礎知識centos下環境搭建

    oracle數據庫 但是 sorted 插入 執行 映射 放松 適合 oracl 1. Redis 與其他 key - value 緩存產品有以下三個特點: Redis支持數據的持久化,可以將內存中的數據保持在磁盤中,重啟的時候可以再次加載進行使用。 Redis不僅僅支持

    四、redis系列主從複製哨兵機制

    1. 緒言   在現實應用環境中,出於資料容量、容災、效能等因素的考慮,往往不會只使用一臺伺服器,而是使用叢集的方式。Redis 中也有類似的維持一主多從的方式提高 Redis 叢集的高可用性的方案,而其中不可避免的則是如何保證主從例項間的資料一致性,複製(Replication)是其解決辦法。本篇介紹re

    四、redis系列主從復制哨兵機制

    添加 連接 管理 block 也有 daemonize toc 導出 拓撲 1. 緒言   在現實應用環境中,出於數據容量、容災、性能等因素的考慮,往往不會只使用一臺服務器,而是使用集群的方式。Redis 中也有類似的維持一主多從的方式提高 Redis 集群的高可用性的方案

    MapReduce系列全域性引數、資料檔案的傳遞引用

    MapReduce程式設計過程中全域性引數、資料檔案的傳遞與引用的主要有一下幾種方法。 1、讀寫HDFS檔案 通過利用Hadoop的Java Apl來實現讀寫HDFS檔案,需要注意的是針對多個Map或Reduce的寫操作會產生衝突,覆蓋原有資料 優點:能夠實現讀寫,也比較直觀 缺點:

    資料庫快取一致性策略

    問題:怎麼保持快取與資料庫一致? 要解答這個問題,我們首先來看不一致的幾種情況。我將不一致分為三種情況: 1. 資料庫有資料,快取沒有資料; 2. 資料庫有資料,快取也有資料,資料不相等; 3. 資料庫沒有資料,快取有資料。 大多數人使用的策略,叫做 Cache Asi

    分散式資料庫快取雙寫一致性方案解析(三)

    作者:孤獨煙出處: http://rjzheng.cnblogs.com/ 正文 博主本來覺得,《分散式之資料庫和快取雙寫一致性方案解析》,一文已經十分清晰。然而這一兩天,有人在微信上私聊我,覺得應該要採用 先刪快取,再更新資料庫,再刪快取 這一方案作為快取更新策

    分散式資料庫快取雙寫一致性方案解析

    為什麼寫這篇文章? 首先,快取由於其高併發和高效能的特性,已經在專案中被廣泛使用。在讀取快取方面,大家沒啥疑問,都是按照下圖的流程來進行業務操作。 但是在更新快取方面,對於更新完資料庫,是更新快取呢,還是刪除快取。又或者是先刪除快取,再更新資料庫,其實大家存在

    Redis系列新增節點刪除節點(四)

    在上一篇文章中,簡單實現了一個Redis叢集,假如需要在叢集中增加或刪除節點,怎麼辦呢,下面以一個例子來紹介下。 1、首先把前面搭建好的叢集啟動起來 longwentaodeMacBook-Pro:redis-cluster longwentao$ cd

    分散式資料庫快取雙寫一致性方案解疑

    在網際網路領域,快取由於其高併發和高效能的特性,已經在專案中被廣泛使用。在讀取快取方面,大家沒什麼疑問,都是按照下圖的流程來進行業務操作。但是在更新快取方面,對於更新完資料庫,是更新快取呢,還是刪除快取;又或者是先刪除快取,再更新資料庫,其實大家存在很大的爭議。目前筆者還沒有

    Redis】3.RedisMySQL資料一致性的思考

    Redis特性 先列舉一下Redis的特點: 讀寫效能優異 持久化 資料型別豐富 單執行緒 資料自動過期 釋出訂閱 分散式 作為快取使用時,一般有兩種方式更新資料: 1、讀取前,先去讀Redis,如果沒有資料,讀取資料庫,將資料拉入Redis。 2、修改資料時