最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

redis緩存與數(shù)據(jù)庫一致性的問題及解決

 更新時間:2023年06月03日 11:02:49   作者:chenshiying007  
這篇文章主要介紹了redis緩存與數(shù)據(jù)庫一致性的問題及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

一、需求起因

假設(shè)先寫數(shù)據(jù)庫,再淘汰緩存:第一步寫數(shù)據(jù)庫操作成功,第二步淘汰緩存失敗,則會出現(xiàn)DB中是新數(shù)據(jù),Cache中是舊數(shù)據(jù),數(shù)據(jù)不一致【db中是新數(shù)據(jù),cache中是舊數(shù)據(jù)】。

假設(shè)先淘汰緩存,再寫數(shù)據(jù)庫:第一步淘汰緩存成功,第二步寫數(shù)據(jù)庫失敗【cache中無數(shù)據(jù),db中是舊數(shù)據(jù)】。

結(jié)論:先淘汰緩存,再寫數(shù)據(jù)庫。

二、數(shù)據(jù)不一致原因

先操作緩存,在寫數(shù)據(jù)庫成功之前,如果有讀請求發(fā)生,可能導致舊數(shù)據(jù)入緩存,引發(fā)數(shù)據(jù)不一致。

寫流程

1)先淘汰cache

2)再寫db

讀流程

1)先讀cache,如果數(shù)據(jù)命中hit則返回

2)如果數(shù)據(jù)未命中miss則讀db

3)將db中讀取出來的數(shù)據(jù)入緩存

什么情況下可能出現(xiàn)緩存和數(shù)據(jù)庫中數(shù)據(jù)不一致呢?

在分布式環(huán)境下,數(shù)據(jù)的讀寫都是并發(fā)的,上游有多個應用,通過一個服務的多個部署(為了保證可用性,一定是部署多份的),對同一個數(shù)據(jù)進行讀寫,在數(shù)據(jù)庫層面并發(fā)的讀寫并不能保證完成順序,也就是說后發(fā)出的讀請求很可能先完成(讀出臟數(shù)據(jù)):

  • a)發(fā)生了寫請求A,A的第一步淘汰了cache(如上圖中的1)
  • b)A的第二步寫數(shù)據(jù)庫,發(fā)出修改請求(如上圖中的2)
  • c)發(fā)生了讀請求B,B的第一步讀取cache,發(fā)現(xiàn)cache中是空的(如上圖中的步驟3)
  • d)B的第二步讀取數(shù)據(jù)庫,發(fā)出讀取請求,此時A的第二步寫數(shù)據(jù)還沒完成,讀出了一個臟數(shù)據(jù)放入cache(如上圖中的步驟4)

即在數(shù)據(jù)庫層面,后發(fā)出的請求4比先發(fā)出的請求2先完成了,讀出了臟數(shù)據(jù),臟數(shù)據(jù)又入了緩存,緩存與數(shù)據(jù)庫中的數(shù)據(jù)不一致出現(xiàn)了

三、問題解決思路

能否做到先發(fā)出的請求一定先執(zhí)行完成呢?常見的思路是“串行化” 

細節(jié)如下:

1)service的上游是多個業(yè)務應用,上游發(fā)起請求對同一個數(shù)據(jù)并發(fā)的進行讀寫操作,上例中并發(fā)進行了一個uid=1的余額修改(寫)操作與uid=1的余額查詢(讀)操作

2)service的下游是數(shù)據(jù)庫DB,假設(shè)只讀寫一個DB

3)中間是服務層service,它又分為了這么幾個部分

  • 3.1)最上層是任務隊列
  • 3.2)中間是工作線程,每個工作線程完成實際的工作任務,典型的工作任務是通過數(shù)據(jù)庫連接池讀寫數(shù)據(jù)庫
  • 3.3)最下層是數(shù)據(jù)庫連接池,所有的SQL語句都是通過數(shù)據(jù)庫連接池發(fā)往數(shù)據(jù)庫去執(zhí)行的

工作線程的典型工作流是這樣的:

void work_thread_routine(){
Task t = TaskQueue.pop(); // 獲取任務
// 任務邏輯處理,生成sql語句
DBConnection c = CPool.GetDBConnection(); // 從DB連接池獲取一個DB連接
c.execSQL(sql); // 通過DB連接執(zhí)行sql語句
CPool.PutDBConnection(c); // 將DB連接放回DB連接池
}

提問:任務隊列其實已經(jīng)做了任務串行化的工作,能否保證任務不并發(fā)執(zhí)行?

答:不行,因為

(1)1個服務有多個工作線程,串行彈出的任務會被并行執(zhí)行

(2)1個服務有多個數(shù)據(jù)庫連接,每個工作線程獲取不同的數(shù)據(jù)庫連接會在DB層面并發(fā)執(zhí)行

提問:假設(shè)服務只部署一份,能否保證任務不并發(fā)執(zhí)行?

答:不行,原因同上

提問:假設(shè)1個服務只有1條數(shù)據(jù)庫連接,能否保證任務不并發(fā)執(zhí)行?

答:不行,因為

(1)1個服務只有1條數(shù)據(jù)庫連接,只能保證在一個服務器上的請求在數(shù)據(jù)庫層面是串行執(zhí)行的

(2)因為服務是分布式部署的,多個服務上的請求在數(shù)據(jù)庫層面仍可能是并發(fā)執(zhí)行的

提問:假設(shè)服務只部署一份,且1個服務只有1條連接,能否保證任務不并發(fā)執(zhí)行?

答:可以,全局來看請求是串行執(zhí)行的,吞吐量很低,并且服務無法保證可用性

完了,看似無望了,

1)任務隊列不能保證串行化

2)單服務多數(shù)據(jù)庫連接不能保證串行化

3)多服務單數(shù)據(jù)庫連接不能保證串行化

4)單服務單數(shù)據(jù)庫連接可能保證串行化,但吞吐量級低,且不能保證服務的可用性,幾乎不可行,那是否還有解?

解決方式

  • 退一步想,其實不需要讓全局的請求串行化,而只需要“讓同一個數(shù)據(jù)的訪問能串行化”就行。
  • 在一個服務內(nèi),如何做到“讓同一個數(shù)據(jù)的訪問串行化”,只需要“讓同一個數(shù)據(jù)的訪問通過同一條DB連接執(zhí)行”就行。
  • 如何做到“讓同一個數(shù)據(jù)的訪問通過同一條DB連接執(zhí)行”,只需要“在DB連接池層面稍微修改,按數(shù)據(jù)取連接即可”
  • 獲取DB連接的CPool.GetDBConnection()【返回任何一個可用DB連接】改為
  • CPool.GetDBConnection(longid)【返回id取模相關(guān)聯(lián)的DB連接】

這個修改的好處是:

1)簡單,只需要修改DB連接池實現(xiàn),以及DB連接獲取處

2)連接池的修改不需要關(guān)注業(yè)務,傳入的id是什么含義連接池不關(guān)注,直接按照id取模返回DB連接即可

3)可以適用多種業(yè)務場景,取用戶數(shù)據(jù)業(yè)務傳入user-id取連接,取訂單數(shù)據(jù)業(yè)務傳入order-id取連接即可

這樣的話,就能夠保證同一個數(shù)據(jù)例如uid在數(shù)據(jù)庫層面的執(zhí)行一定是串行的

稍等稍等,服務可是部署了很多份的,上述方案只能保證同一個數(shù)據(jù)在一個服務上的訪問,在DB層面的執(zhí)行是串行化的,實際上服務是分布式部署的,在全局范圍內(nèi)的訪問仍是并行的,怎么解決呢?能不能做到同一個數(shù)據(jù)的訪問一定落到同一個服務呢?

能否做到同一個數(shù)據(jù)的訪問落在同一個服務上?

上面分析了服務層service的上下游及內(nèi)部結(jié)構(gòu),再一起看一下應用層上下游及內(nèi)部結(jié)構(gòu) 

上圖是一個業(yè)務應用的上下游及服務內(nèi)部詳細展開,細節(jié)如下:

1)業(yè)務應用的上游不確定是啥,可能是直接是http請求,可能也是一個服務的上游調(diào)用

2)業(yè)務應用的下游是多個服務service

3)中間是業(yè)務應用,它又分為了這么幾個部分

  • 3.1)最上層是任務隊列【或許web-server例如tomcat幫你干了這個事情了】
  • 3.2)中間是工作線程【或許web-server的工作線程或者cgi工作線程幫你干了線程分派這個事情了】,每個工作線程完成實際的業(yè)務任務,典型的工作任務是通過服務連接池進行RPC調(diào)用
  • 3.3)最下層是服務連接池,所有的RPC調(diào)用都是通過服務連接池往下游服務去發(fā)包執(zhí)行的

工作線程的典型工作流是這樣的:

voidwork_thread_routine(){
Task t = TaskQueue.pop(); // 獲取任務
// 任務邏輯處理,組成一個網(wǎng)絡(luò)包packet,調(diào)用下游RPC接口
ServiceConnection c = CPool.GetServiceConnection(); // 從Service連接池獲取一個Service連接
c.Send(packet); // 通過Service連接發(fā)送報文執(zhí)行RPC請求
CPool.PutServiceConnection(c); // 將Service連接放回Service連接池
}

似曾相識吧?沒錯,只要對服務連接池進行少量改動:

獲取Service連接的CPool.GetServiceConnection()【返回任何一個可用Service連接】改為CPool.GetServiceConnection(longid)【返回id取模相關(guān)聯(lián)的Service連接】這樣的話,就能夠保證同一個數(shù)據(jù)例如uid的請求落到同一個服務Service上。

由于數(shù)據(jù)庫層面的讀寫并發(fā),引發(fā)的數(shù)據(jù)庫與緩存數(shù)據(jù)不一致的問題(本質(zhì)是后發(fā)生的讀請求先返回了),可能通過兩個小的改動解決:

1)修改服務Service連接池,id取模選取服務連接,能夠保證同一個數(shù)據(jù)的讀寫都落在同一個后端服務上

2)修改數(shù)據(jù)庫DB連接池,id取模選取DB連接,能夠保證同一個數(shù)據(jù)的讀寫在數(shù)據(jù)庫層面是串行的

四、總結(jié)

本篇文章的作用在于提出分布式緩存的問題

實現(xiàn)思路還可以是分布式鎖,這里只是拋磚引玉

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Redis 哨兵與集群腦裂問題及其解決

    Redis 哨兵與集群腦裂問題及其解決

    本文主要介紹了Redis 哨兵與集群腦裂問題及其解決,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2025-04-04
  • 淺析Redis中String數(shù)據(jù)類型及其底層編碼

    淺析Redis中String數(shù)據(jù)類型及其底層編碼

    這篇文章主要介紹?Redis?中?String?數(shù)據(jù)類型及其底層編碼,文中有詳細的代碼示例,對大家的工作及學習有一定的幫助,需要的朋友可以參考下
    2023-05-05
  • redis如何設(shè)置database個數(shù)

    redis如何設(shè)置database個數(shù)

    這篇文章主要介紹了redis如何設(shè)置database個數(shù)的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • redis?zrange?與?zrangebyscore的區(qū)別解析

    redis?zrange?與?zrangebyscore的區(qū)別解析

    這篇文章主要介紹了redis?zrange與zrangebyscore的區(qū)別,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-06-06
  • 解決Redis分布式鎖的誤刪問題和原子性問題

    解決Redis分布式鎖的誤刪問題和原子性問題

    Redis的分布式鎖是通過利用Redis的原子操作和特性來實現(xiàn)的,為了保證數(shù)據(jù)的一致性和避免沖突,可以使用分布式鎖來進行同步控制,本文給大家介紹了如何解決Redis分布式鎖的誤刪問題和原子性問題,需要的朋友可以參考下
    2024-02-02
  • Redis是如何高效管理有限內(nèi)存的(方法詳解)

    Redis是如何高效管理有限內(nèi)存的(方法詳解)

    過期刪除策略的深度剖析 Redis 可以對 key 設(shè)置過期時間的,為了防止過期的key長期占用內(nèi)存,需要相應的過期刪除策略將過期的key刪除 基礎(chǔ)操作 Redis設(shè)置過期時間 setex key1 5 value1:創(chuàng)建記錄的時候指定過期時間,設(shè)置key1在5秒后過期 其實Redis這是一種基于創(chuàng)
    2025-09-09
  • Redis 哨兵高模式搭建及Java代碼配置

    Redis 哨兵高模式搭建及Java代碼配置

    這篇文章主要介紹了Redis 哨兵高模式搭建及Java代碼配置,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-12-12
  • redis的string類型及bitmap介紹

    redis的string類型及bitmap介紹

    這篇文章主要介紹了redis的string類型及bitmap介紹,redis有很多的客戶端連接進來,站在redis所在機器的角度來說,就是有很多socket的連接
    2022-07-07
  • redis實現(xiàn)紅鎖的示例代碼

    redis實現(xiàn)紅鎖的示例代碼

    在分布式系統(tǒng)中,實現(xiàn)一個可靠的鎖機制是非常重要的,本文主要介紹了redis實現(xiàn)紅鎖的示例代碼,具有一定的參考價值,感興趣的可以了解一下
    2025-04-04
  • Redis集群增加節(jié)點與刪除節(jié)點的方法詳解

    Redis集群增加節(jié)點與刪除節(jié)點的方法詳解

    這篇文章主要給大家介紹了關(guān)于Redis集群增加節(jié)點與刪除節(jié)點的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用Redis具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-09-09

最新評論

临湘市| 陵水| 普宁市| 闽清县| 襄垣县| 澳门| 涞水县| 浮山县| 改则县| 汉寿县| 西乡县| 石城县| 靖边县| 类乌齐县| 青海省| 武穴市| 大渡口区| 承德市| 观塘区| 长治县| 阿图什市| 河南省| 闵行区| 建湖县| 利辛县| 太仓市| 鄂尔多斯市| 扶余县| 且末县| 台前县| 三江| 全州县| 扶余县| 称多县| 汉川市| 安多县| 榆树市| 吉安县| 辽宁省| 锡林浩特市| 镇平县|