欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

redis分布式鎖解決緩存雙寫一致性

 更新時(shí)間:2023年05月08日 15:21:00   作者:我是小趴菜  
這篇文章主要為大家介紹了redis分布式鎖解決緩存雙寫一致性示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

如何解決緩存雙寫問題

只要涉及到緩存,那么緩存雙寫的問題就避免不了,每一種情況下使用的方案也不相同,對(duì)于數(shù)據(jù)一致性要求不高的場(chǎng)景,我們可以使用延時(shí)雙刪等方案來實(shí)現(xiàn),而對(duì)于一致性要求很高的場(chǎng)景,在之前查找的資料都是基于隊(duì)列來實(shí)現(xiàn),也就是所有的請(qǐng)求都進(jìn)入一個(gè)隊(duì)列,但是實(shí)現(xiàn)起來相對(duì)來說比較復(fù)雜。今天就使用分布式鎖來實(shí)現(xiàn)

業(yè)務(wù)背景-美食分享

1: 現(xiàn)在有一個(gè)很火的美食博主分享了一篇美食,此刻是很多人都會(huì)來查看,對(duì)于美食分享是典型的讀多寫少的場(chǎng)景,可以利用緩存

//根據(jù)id查詢美食信息  
public GoodsVO loadGoodsInfoById(Long id) {
  //從redis中拿用戶信息
  Object obj = redisTemplate.opsForValue().get(GOODS_KEY + id);
  if(obj == null) {
      //如果redis中不存在,就從數(shù)據(jù)庫(kù)中獲取
      GoodsVO goods = loadGoodsFromDb(id);
      //將結(jié)果保存到redis中
      redisTemplate.opsForValue().set(GOODS_KEY+id,JSONUtil.toJsonPrettyStr(goods));
      return fileUser;
  }
  return JSONUtil.toBean(obj.toString(), GoodsVO.class);
}
//編輯美食信息
public void modifyGoodsById(GoodsVO goodsVO) throws Exception{
    //刪除緩存
    redisTemplate.delete(GOODS_KEY + goodsVO.getId());
    //更新用戶信息
    goodsMapper.updateById(goodsVO);
    //刪除緩存
    Thread.sleep(1000);
    redisTemplate.delete(GOODS_KEY + goodsVO.getId());
}

而對(duì)于更新數(shù)據(jù)的時(shí)候是先更新緩存還是先更新數(shù)據(jù)庫(kù)呢?

1:先更新緩存再更新數(shù)據(jù)庫(kù)

  • 如果更新緩存成功,更新數(shù)據(jù)庫(kù)失敗,那么緩存中的數(shù)據(jù)就是臟數(shù)據(jù),與數(shù)據(jù)庫(kù)數(shù)據(jù)不一致

2:先更新數(shù)據(jù)庫(kù)再更新緩存

  • 更新數(shù)據(jù)庫(kù)成功,但是此時(shí)更新緩存失敗,那么緩存中的還是臟數(shù)據(jù),與數(shù)據(jù)庫(kù)數(shù)據(jù)不一致

3:延時(shí)雙刪 - 先刪除緩存,再更新數(shù)據(jù)庫(kù),最后再刪除緩存

  • 即使第一次刪除緩存失敗了,后續(xù)的更新數(shù)據(jù)庫(kù)操作也不會(huì)執(zhí)行,此時(shí)來讀取的線程發(fā)現(xiàn)緩存中沒有數(shù)據(jù),從數(shù)據(jù)庫(kù)讀取最新的。
  • 第一次刪除緩存成功,更新數(shù)據(jù)庫(kù)失敗,此時(shí)來讀取的線程發(fā)現(xiàn)緩存中沒有數(shù)據(jù),從數(shù)據(jù)庫(kù)讀取最新的。
  • 第一次刪除緩存成功,更新數(shù)據(jù)庫(kù)成功,第二次刪除緩存成功,那么來讀取的線程發(fā)現(xiàn)緩存中沒有數(shù)據(jù),從數(shù)據(jù)庫(kù)讀取最新的。
  • 第一次刪除除緩存成功,更新數(shù)據(jù)庫(kù)成功,第二次刪除緩存失敗,這時(shí)候就會(huì)有問題了

問題一:Thread-A第一次刪除緩存成功,然后更新數(shù)據(jù),但是這時(shí)候不知道怎么了,可能是線程阻塞了或者其它原因,導(dǎo)致還沒有開始更新數(shù)據(jù)庫(kù),這時(shí)候另外一個(gè)線程Thread-B來讀取數(shù)據(jù)了,讀取到數(shù)據(jù)之后將數(shù)據(jù)放到緩存中,這時(shí)候Thread-A才開始更新數(shù)據(jù)庫(kù),但是Thread-A在第二次刪除緩存的時(shí)候失敗了,此時(shí)就導(dǎo)致緩存中的數(shù)據(jù)是之前的舊數(shù)據(jù),與數(shù)據(jù)庫(kù)的數(shù)據(jù)不一致

問題二:假設(shè)修改的時(shí)候都沒問題,第二次刪除的緩存的時(shí)候都正常,這時(shí)候讀取數(shù)據(jù)的時(shí)候緩存里面肯定就沒有了,這時(shí)候就要從數(shù)據(jù)庫(kù)讀取,如果這時(shí)候一下子并發(fā)量很高,那么這些線程都要從數(shù)據(jù)庫(kù)中查詢,這時(shí)候數(shù)據(jù)庫(kù)都有可能直接掛掉

分布式鎖

查詢

//根據(jù)id查詢美食信息  
public GoodsVO loadGoodsInfoById(Long id) {
  //從redis中拿用戶信息
  Object obj = redisTemplate.opsForValue().get(GOODS_KEY + id);
  if(obj == null) {
      //如果redis中不存在,就從數(shù)據(jù)庫(kù)中獲取
      GoodsVO goods = loadGoodsFromDb(id);
      return fileUser;
  }
  return JSONUtil.toBean(obj.toString(), GoodsVO.class);
}
//查詢緩存中沒有,這時(shí)候就要去數(shù)據(jù)庫(kù)中查詢
public String getGoodInfoFromDb(Long id) {
    //此時(shí)這里要加的鎖和 modifyGoodsById()方法加的應(yīng)該是同一把鎖,這樣才能保證雙寫一致性
    boolean lock = redisLock.tryLock(GOODS_KEY+id);
    if(!lock) {
    	//獲取鎖失敗,嘗試從緩存中獲取
    	Object obj = redisTemplate.opsForValue().get(GOODS_KEY + id);
      if(obj != null){
          return JSONUtil.toBean(obj.toString(), GoodsVO.class);
      }else{
          //直接返回一個(gè)操作頻繁的信息給前端
          throw new GloableException("訪問頻繁,請(qǐng)稍后再試");
      }
    }
    try{
        //獲取到鎖了
        //嘗試從緩存中拿
        Object obj = redisTemplate.opsForValue().get(GOODS_KEY + id);
        if(obj != null) {
            return JSONUtil.toBean(obj.toString(), GoodsVO.class);
        }
        //緩存中沒有,從MySql中拿
        GoodsVO goods = goodsMapper.selectById(id);
        if(goods == null) {
            //為了解決緩存穿透的問題
            redisTemplate.opsForValue().set(GOODS_NULL_KEY + id);
            return null;
        }
        //將數(shù)據(jù)庫(kù)查到的數(shù)據(jù)放到緩存中
        redisTemplate.opsForValue().set(GOODS_KEY+id,JSONUtil.toJsonPrettyStr(goods));
        return goods;
    }finally{
       //釋放鎖
       redisLock.unLock(GOODS_KEY+goodsVO.getId());
    }
}

修改

//編輯美食信息
public void modifyGoodsById(GoodsVO goodsVO) throws Exception{
    //嘗試獲取鎖
    boolean lock = redisLock.tryLock(GOODS_KEY+goodsVO.getId());
    if(!lock) {
      	//直接返回一個(gè)操作頻繁的信息給前端
        throw new GloableException("更新數(shù)據(jù)失敗,請(qǐng)稍后再試.....");
    }
    try{
        //獲取到鎖了
        //更新用戶信息
        goodsMapper.updateById(goodsVO);
        //更新完之后,將數(shù)據(jù)庫(kù)最新的數(shù)據(jù)更新到緩存中
        redisTemplate.opsForValue().set(GOODS_KEY+id,JSONUtil.toJsonPrettyStr(goodsVO));
    }finally{
        //釋放鎖
        redisLock.unLock(GOODS_KEY+goodsVO.getId());
    }
}

現(xiàn)在第一次來訪問數(shù)據(jù),返現(xiàn)Redis中沒有,這時(shí)候就會(huì)去MySql中查,但是只有獲取到鎖的線程才可以去數(shù)據(jù)庫(kù)中查,所以此時(shí)只有一個(gè)線程訪問數(shù)據(jù)庫(kù),這時(shí)候即使有線程要去修改數(shù)據(jù),由于鎖已經(jīng)被拿走了,無(wú)法獲取到鎖,也就無(wú)法修改,保證了數(shù)據(jù)一致性。

假設(shè)現(xiàn)在修改的線程獲取到鎖了。由于之前Redis中已經(jīng)有數(shù)據(jù)了,此時(shí)所有讀取數(shù)據(jù)的線程都從Redis中拿,當(dāng)修改完數(shù)據(jù)之后,重新設(shè)置緩存,此時(shí)緩存中的數(shù)據(jù)就是最新的

以上就是分布式鎖解決緩存雙寫一致性的詳細(xì)內(nèi)容,更多關(guān)于分布式鎖解決緩存雙寫一致性的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Redis緩存穿透出現(xiàn)原因及解決方案

    Redis緩存穿透出現(xiàn)原因及解決方案

    這篇文章主要介紹了Redis緩存穿透出現(xiàn)原因及解決方案,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • Redis?Lua同步鎖實(shí)現(xiàn)源碼解析

    Redis?Lua同步鎖實(shí)現(xiàn)源碼解析

    這篇文章主要為大家介紹了Redis?Lua同步鎖實(shí)現(xiàn)源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-05-05
  • redis使用watch秒殺搶購(gòu)實(shí)現(xiàn)思路

    redis使用watch秒殺搶購(gòu)實(shí)現(xiàn)思路

    這篇文章主要為大家詳細(xì)介紹了redis使用watch秒殺搶購(gòu)的實(shí)現(xiàn)思路,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-02-02
  • 基于Redis位圖實(shí)現(xiàn)用戶簽到功能

    基于Redis位圖實(shí)現(xiàn)用戶簽到功能

    這篇文章主要介紹了基于Redis位圖實(shí)現(xiàn)用戶簽到功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-05-05
  • Redis配置文件詳解

    Redis配置文件詳解

    這篇文章主要介紹了Redis配置文件詳解,本文詳細(xì)完整的用中文解釋了Redis配置文件中各種參數(shù)的作用和功能,需要的朋友可以參考下
    2015-04-04
  • redis 主從備份及其主備切換的操作

    redis 主從備份及其主備切換的操作

    這篇文章主要介紹了redis 主從備份及其主備切換的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • 簡(jiǎn)介Redis中的showlog功能

    簡(jiǎn)介Redis中的showlog功能

    這篇文章主要介紹了簡(jiǎn)介Redis中的showlog功能,作者同時(shí)對(duì)比了DEL命令的性能,需要的朋友可以參考下
    2015-06-06
  • Redis分析慢查詢操作的實(shí)例教程

    Redis分析慢查詢操作的實(shí)例教程

    這篇文章主要給大家介紹了關(guān)于Redis如何分析慢查詢操作的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-09-09
  • Redis優(yōu)化token校驗(yàn)主動(dòng)失效的實(shí)現(xiàn)方案

    Redis優(yōu)化token校驗(yàn)主動(dòng)失效的實(shí)現(xiàn)方案

    在普通的token頒發(fā)和校驗(yàn)中 當(dāng)用戶發(fā)現(xiàn)自己賬號(hào)和密碼被暴露了時(shí)修改了登錄密碼后舊的token仍然可以通過系統(tǒng)校驗(yàn)直至token到達(dá)失效時(shí)間,所以系統(tǒng)需要token主動(dòng)失效的一種能力,所以本文給大家介紹了Redis優(yōu)化token校驗(yàn)主動(dòng)失效的實(shí)現(xiàn)方案,需要的朋友可以參考下
    2024-03-03
  • redis搭建哨兵模式實(shí)現(xiàn)一主兩從三哨兵

    redis搭建哨兵模式實(shí)現(xiàn)一主兩從三哨兵

    本文主要介紹了redis搭建哨兵模式實(shí)現(xiàn)一主兩從三哨兵,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-08-08

最新評(píng)論