亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解Mysql中保證緩存與數(shù)據(jù)庫(kù)的雙寫一致性

 更新時(shí)間:2024年03月27日 10:42:45   作者:開心就好啦啦啦  
在一些高并發(fā)場(chǎng)景下,為了提升系統(tǒng)的性能,我們通常會(huì)將數(shù)據(jù)存儲(chǔ)在 Redis 緩存中,并通過 Redis 緩存來提高系統(tǒng)的讀取速度,這篇文章主要介紹了詳解Mysql中保證緩存與數(shù)據(jù)庫(kù)的雙寫一致性,需要的朋友可以參考下

概述

MySQL 和 Redis 都是常見的數(shù)據(jù)存儲(chǔ)方案,MySQL 用于存儲(chǔ)結(jié)構(gòu)化數(shù)據(jù),Redis 用于存儲(chǔ)非結(jié)構(gòu)化數(shù)據(jù)。在一些高并發(fā)場(chǎng)景下,為了提升系統(tǒng)的性能,我們通常會(huì)將數(shù)據(jù)存儲(chǔ)在 Redis 緩存中,并通過 Redis 緩存來提高系統(tǒng)的讀取速度。但是,Redis 緩存中的數(shù)據(jù)是不穩(wěn)定的,可能會(huì)隨時(shí)被刪除或者被更新,因此需要和 MySQL 中的數(shù)據(jù)進(jìn)行同步,保證數(shù)據(jù)的一致性。

但是使用過緩存的人都應(yīng)該知道,在實(shí)際應(yīng)用場(chǎng)景中,要想實(shí)時(shí)刻保證緩存和數(shù)據(jù)庫(kù)中的數(shù)據(jù)一樣,很難做到。 基本上都是盡可能讓他們的數(shù)據(jù)在絕大部分時(shí)間內(nèi)保持一致,并保證最終是一致的。

同步策略

首先介紹一下雙寫一致性·當(dāng)修改了數(shù)據(jù)庫(kù)的數(shù)據(jù)也要同時(shí)更新緩存的數(shù)據(jù),緩存和數(shù)據(jù)庫(kù)

四種同步策略:
想要保證緩存與數(shù)據(jù)庫(kù)的雙寫一致,一共有4種方式,即4種同步策略:
1. 先更新緩存,再更新數(shù)據(jù)庫(kù);
2. 先更新數(shù)據(jù)庫(kù),再更新緩存;
3. 先刪除緩存,再更新數(shù)據(jù)庫(kù);
4. 先更新數(shù)據(jù)庫(kù),再刪除緩存。

從這4種同步策略中,我們需要作出比較的是:

  • 更新緩存與刪除緩存哪種方式更合適?
  • 應(yīng)該先操作數(shù)據(jù)庫(kù)還是先操作緩存?

更新緩存還是刪除緩存:

下面,我們來分析一下,應(yīng)該采用更新緩存還是刪除緩存的方式。

1.更新緩存

  • 優(yōu)點(diǎn):每次數(shù)據(jù)變化都及時(shí)更新緩存,所以查詢時(shí)不容易出現(xiàn)未命中的情況。
  • 缺點(diǎn):更新緩存的消耗比較大。如果數(shù)據(jù)需要經(jīng)過復(fù)雜的計(jì)算再寫入緩存,那么頻繁的更新緩存,就會(huì)影響服務(wù)器的性能。如果是寫入數(shù)據(jù)頻繁的業(yè)務(wù)場(chǎng)景,那么可能頻繁的更新緩存時(shí),卻沒有業(yè)務(wù)讀取該數(shù)據(jù)。

2.刪除緩存

  • 優(yōu)點(diǎn):操作簡(jiǎn)單,無論更新操作是否復(fù)雜,都是將緩存中的數(shù)據(jù)直接刪除。
  • 缺點(diǎn):刪除緩存后,下一次查詢緩存會(huì)出現(xiàn)未命中,這時(shí)需要重新讀取一次數(shù)據(jù)庫(kù)。

從上面的比較來看,一般情況下,刪除緩存是更優(yōu)的方案。

先操作數(shù)據(jù)庫(kù)還是緩存:

下面,我們?cè)賮矸治鲆幌?,?yīng)該先操作數(shù)據(jù)庫(kù)還是先操作緩存。

案例一、先刪除緩存,在更新數(shù)據(jù)庫(kù)

初始時(shí),緩存和數(shù)據(jù)庫(kù)均為10。

如上圖,先刪除緩存,再更新數(shù)據(jù)庫(kù),可能會(huì)出現(xiàn)的問題:

  • 線程1刪除緩存
  • 線程2查詢緩存未命中,查詢數(shù)據(jù)庫(kù)
  • 寫入緩存的值為10,
  • 線程1再進(jìn)行更新數(shù)據(jù)庫(kù),值為20

此時(shí)數(shù)據(jù)庫(kù)為更新過的值20,而緩存還是舊值10,此時(shí)出現(xiàn)了數(shù)據(jù)庫(kù)和緩存數(shù)據(jù)不一致情況。

案例二 先操作數(shù)據(jù)庫(kù),再刪除緩存

如上圖,先刪除緩存,再更新數(shù)據(jù)庫(kù),可能會(huì)出現(xiàn)的問題:

  • 線程1查詢緩存未命中,查詢數(shù)據(jù)庫(kù)
  • 線程2更新數(shù)據(jù)庫(kù)為20,
  • 線程2刪除緩存
  • 線程1寫入緩存值為10

此時(shí)數(shù)據(jù)庫(kù)為更新過的值20,而緩存還是舊值10,此時(shí)出現(xiàn)了數(shù)據(jù)庫(kù)和緩存數(shù)據(jù)不一致情況。

經(jīng)過案例一和案例二的比較,先刪除緩存和先更新數(shù)據(jù)庫(kù)都會(huì)出現(xiàn)問題。

延時(shí)雙刪策略(不推薦)

在寫庫(kù)前后都進(jìn)行redis.del(key)操作,并且設(shè)定合理的超時(shí)時(shí)間。

偽代碼如下:

public void write( String key, Object data ){
    redis.delKey(key);
    db.updateData(data); 
    Thread.sleep(500);
    redis.delKey(key);
}

問題:這個(gè)500毫秒怎么確定的,具體該休眠多久時(shí)間呢?

需要評(píng)估自己的項(xiàng)目的讀數(shù)據(jù)業(yè)務(wù)邏輯的耗時(shí)。這么做的目的,就是確保讀請(qǐng)求結(jié)束,寫請(qǐng)求可以刪除讀請(qǐng)求造成的緩存臟數(shù)據(jù)。當(dāng)然這種策略還要考慮redis和數(shù)據(jù)庫(kù)主從同步的耗時(shí)。另外這種策略也會(huì)可能會(huì)有臟數(shù)據(jù)的風(fēng)險(xiǎn),而且還會(huì)消耗不必要的性能。

在實(shí)際場(chǎng)景中,并不推薦延時(shí)雙刪策略,一方面可能會(huì)有臟數(shù)據(jù)的風(fēng)險(xiǎn),而且還會(huì)消耗不必要的性能。

雖然先更新數(shù)據(jù)庫(kù),再刪除緩存也是會(huì)出現(xiàn)數(shù)據(jù)不一致性的問題,但是在實(shí)際中,這個(gè)問題出現(xiàn)的概率并不高。因?yàn)榫彺娴膶懭胪ǔRh(yuǎn)遠(yuǎn)快于數(shù)據(jù)庫(kù)的寫入,所以在實(shí)際中很難出現(xiàn)請(qǐng)求 B 已經(jīng)更新了數(shù)據(jù)庫(kù)并且刪除了緩存,請(qǐng)求 A 才更新完緩存的情況。所以,「先更新數(shù)據(jù)庫(kù) + 再刪除緩存」的方案,是可以保證數(shù)據(jù)一致性的。

但是,為了確保萬無一失,在更新完緩存時(shí),給緩存加上較短的過期時(shí)間,這樣即時(shí)出現(xiàn)緩存不一致的情況,緩存的數(shù)據(jù)也會(huì)很快過期,對(duì)業(yè)務(wù)還是能接受的。另外在更新緩存中加入過期時(shí)間,這樣就算出現(xiàn)了緩存和數(shù)據(jù)庫(kù)不一致問題,但最終是一致的。

使用分布式鎖實(shí)現(xiàn)雙寫一致性

分別在寫數(shù)據(jù)和讀數(shù)據(jù)加分布式鎖,保證同一時(shí)間只運(yùn)行一個(gè)請(qǐng)求更新緩存(保證讀寫串行化),就會(huì)不會(huì)產(chǎn)生并發(fā)問題了,這樣就能保證redis和mysql的數(shù)據(jù)強(qiáng)一致性。

但是這樣的話讀操作和寫操作都需要加鎖,效率就會(huì)大大降低。其實(shí)在真實(shí)場(chǎng)景中放入緩存中的數(shù)據(jù)一般是讀多寫少,如果是讀少寫多,那完全可以不用緩存,直接操作數(shù)據(jù)庫(kù)了。

使用讀寫鎖實(shí)現(xiàn)雙寫一致性

在讀多寫少的場(chǎng)景下,可以使用讀鎖和寫鎖的機(jī)制。

  • 共享鎖:讀鎖readLock,加鎖之后,其他線程可以共享讀操作,寫互斥
  • 排他鎖:獨(dú)占鎖writeLock也加寫鎖,加鎖之后,堵塞其他線程讀寫操作。

使用redisson中的讀寫鎖實(shí)現(xiàn)雙寫一致性

想要拿到共享鎖或者排他鎖,都需要先拿到讀寫鎖。通過固定代碼可以拿到讀寫鎖。

RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("READ_WRITE_LOCK");

隨后分別拿到共享鎖和排他鎖。(注意兩個(gè)鎖需要是同一把讀寫鎖)

RLock readLock = readWriteLock.readLock();
RLock writeLock = readWriteLock.writeLock();

讀操作加入讀鎖(共享鎖)

public void getById(Integer id){
  RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("READ_WRITE_LOCK");
  RLock readLock = readWriteLock.readLock();
  try{
    readLock.lock();
    System.out.println("readLock...");
    Item item = (Item) redisTemplate.opsForValue().get("item"+id);
    if(item != null){
      return item;
    }
    item = new Item(id, "手機(jī)", "手機(jī)", 60.00);
    redisTemplate.opsForValue().set("item"+id, item);
    return item;
  }finally{
    readLock.unlock();
  }
}

寫操作加入寫鎖(排他鎖)

public void updateById(Integer id){
  RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("READ_WRITE_LOCK");
  RLock writeLock = readWriteLock.writeLock();
  try{
    writeLock.lock();
    System.out.println("writeLock...");
    Item item = new Item(id, "手機(jī)", "手機(jī)", 100.00);
    try{
      Thread.sleep(2000);
    }catch(InterruptedException e){
      e.printStackTrace();
    }
    redisTemplate.delete("item"+id);
  }finally{
    writeLock.unlock();
  }
}

可以實(shí)現(xiàn)強(qiáng)一致性方案,雖然比分布式鎖好一點(diǎn),但是在高并發(fā)場(chǎng)景下性能也比較低。

使用消息隊(duì)列異步通知

如果允許緩存中的數(shù)據(jù)在短時(shí)間內(nèi)可以跟數(shù)據(jù)庫(kù)數(shù)據(jù)不一致的情況下,可以使用異步通知的方案,可以保證最終一致性。

為了解決雙寫一致性的問題,我們可以引入消息隊(duì)列,比如RabbitMQ,來異步更新Redis。將操作同一資源的請(qǐng)求,打到同一個(gè)隊(duì)列中。

當(dāng)有數(shù)據(jù)變動(dòng)時(shí),我們先操作數(shù)據(jù)庫(kù),然后通過消息隊(duì)列發(fā)送消息到一個(gè)緩存更新的隊(duì)列中,異步更新緩存。這種方式能夠讓寫操作變得更加高效,并且避免了高并發(fā)下的緩存與數(shù)據(jù)庫(kù)數(shù)據(jù)不一致的問題。

訂閱Mysql的Binlog文件(可借助Canal來進(jìn)行)

另一種更為可靠的方法是使用MySQL的binlog。我們可以使用Maxwell或者Canal等工具,實(shí)時(shí)解析binlog,然后更新Redis。

這種方案的好處是即使應(yīng)用程序崩潰,也不會(huì)丟失binlog,因此能夠保證最終的數(shù)據(jù)一致性。但是,這種方案的實(shí)現(xiàn)比較復(fù)雜,需要對(duì)MySQL的內(nèi)部機(jī)制有深入的理解。

總結(jié)

允許延時(shí)一致的業(yè)務(wù),采用異步通知

  • 使用MQ中間件,更新數(shù)據(jù)之后,通知緩存更新,將操作同一資源的請(qǐng)求,打到同一個(gè)隊(duì)列中。
  • 利用canal中間件,不需要修改業(yè)務(wù)代碼,偽裝為mysql的一個(gè)從節(jié)點(diǎn),canal通過讀取binlog數(shù)據(jù)更新緩存

強(qiáng)一致性,采用Redisson提供的讀寫過

在讀多寫少的場(chǎng)景下,可以使用讀鎖和寫鎖的機(jī)制。

  • 共享鎖:讀鎖readLock,加鎖之后,其他線程可以共享讀操作,寫互斥
  • 排他鎖:獨(dú)占鎖writeLock也加寫鎖,加鎖之后,堵塞其他線程讀寫操作。

到此這篇關(guān)于Mysql中如何保證緩存與數(shù)據(jù)庫(kù)的雙寫一致性的文章就介紹到這了,更多相關(guān)保證緩存與數(shù)據(jù)庫(kù)的雙寫一致性內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • MySQL中實(shí)現(xiàn)分頁(yè)操作的實(shí)戰(zhàn)指南

    MySQL中實(shí)現(xiàn)分頁(yè)操作的實(shí)戰(zhàn)指南

    MySQL的分頁(yè)似乎一直是個(gè)問題,下面這篇文章主要給大家介紹了關(guān)于MySQL中實(shí)現(xiàn)分頁(yè)操作的相關(guān)資料,文中通過圖文以及實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-08-08
  • ubuntu 15.04下mysql開放遠(yuǎn)程3306端口

    ubuntu 15.04下mysql開放遠(yuǎn)程3306端口

    這篇文章主要為大家詳細(xì)介紹了ubuntu 15.04開放mysql遠(yuǎn)程3306端口的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-01-01
  • MySQL實(shí)現(xiàn)查詢某個(gè)字段含有字母數(shù)字的值

    MySQL實(shí)現(xiàn)查詢某個(gè)字段含有字母數(shù)字的值

    這篇文章主要介紹了MySQL實(shí)現(xiàn)查詢某個(gè)字段含有字母數(shù)字的值方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • mysql存儲(chǔ)過程 在動(dòng)態(tài)SQL內(nèi)獲取返回值的方法詳解

    mysql存儲(chǔ)過程 在動(dòng)態(tài)SQL內(nèi)獲取返回值的方法詳解

    本篇文章是對(duì)mysql存儲(chǔ)過程在動(dòng)態(tài)SQL內(nèi)獲取返回值進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-06-06
  • mysql8.0無法使用group by的問題及解決

    mysql8.0無法使用group by的問題及解決

    這篇文章主要介紹了mysql8.0無法使用group by的問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • Mysql全文搜索match against的用法

    Mysql全文搜索match against的用法

    全文檢索在 MySQL 中就是一個(gè) FULLTEXT 類型索引。FULLTEXT 索引用于 MyISAM 表,可以在 CREATE TABLE 時(shí)或之后使用 ALTER TABLE 或 CREATE INDEX 在 CHAR、 VARCHAR 或 TEXT 列上創(chuàng)建
    2011-10-10
  • MySQL?表分區(qū)步驟示例詳解

    MySQL?表分區(qū)步驟示例詳解

    MySQL表分區(qū)是一種數(shù)據(jù)庫(kù)管理技術(shù),用于將大型表拆分成更小、更可管理的分區(qū)(子表,這篇文章主要介紹了MySQL?表分區(qū)簡(jiǎn)介,需要的朋友可以參考下
    2023-09-09
  • macOS安裝Solr并索引MySQL

    macOS安裝Solr并索引MySQL

    這篇文章主要介紹了macOS安裝Solr并索引MySQL的相關(guān)資料,非常不錯(cuò)具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-11-11
  • 全面了解MySql中的事務(wù)

    全面了解MySql中的事務(wù)

    下面小編就為大家?guī)硪黄媪私釳ySql中的事務(wù)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-06-06
  • mysql 8.0.18各版本安裝及安裝中出現(xiàn)的問題(精華總結(jié))

    mysql 8.0.18各版本安裝及安裝中出現(xiàn)的問題(精華總結(jié))

    這篇文章主要介紹了mysql 8.0.18各版本安裝及安裝中出現(xiàn)的問題,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-12-12

最新評(píng)論