国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

經(jīng)常用Redis,這些坑你知道嗎?

數(shù)據(jù)庫 其他數(shù)據(jù)庫 Redis
近些年,Redis憑借在性能、穩(wěn)定性和高可擴展性上的卓越表現(xiàn),基本上已經(jīng)成了互聯(lián)網(wǎng)行業(yè)緩存中間件的標(biāo)配,甚至很多傳統(tǒng)行業(yè)也在使用Redis。那么我們在使用Redis等緩存中間件時,要注意哪些問題呢?本文咱們就來聊聊,我們使用緩存中間件過程中曾經(jīng)遇到的坑!

近些年,Redis憑借在性能、穩(wěn)定性和高可擴展性上的卓越表現(xiàn),基本上已經(jīng)成了互聯(lián)網(wǎng)行業(yè)緩存中間件的標(biāo)配,甚至很多傳統(tǒng)行業(yè)也在使用Redis。那么我們在使用Redis等緩存中間件時,要注意哪些問題呢?本文咱們就來聊聊,我們使用緩存中間件過程中曾經(jīng)遇到的坑!

緩存穿透

先看一個常見的緩存使用方式。請求來了,先查緩存,緩存有值就直接返回;緩存沒值,查數(shù)據(jù)庫,然后把數(shù)據(jù)庫的值存到緩存,再返回。

假如緩存沒查到某個值,查數(shù)據(jù)庫也沒這個值,也就是說要查的值根本不存在,這樣就會導(dǎo)致每次對這個值的查詢請求都會穿透到數(shù)據(jù)庫。這就是所謂的“緩存穿透”。

如何避免緩存穿透?

如果從數(shù)據(jù)庫中沒查到值,可以在緩存中記錄一個空值,來避免“緩存穿透”。并且要給這個空值設(shè)置一個較短的過期時間。

比如說,我們經(jīng)常會把用戶信息緩存到Redis。如果調(diào)用方傳了一個不存在的UserID,在緩存中就查不到這個用戶信息,然后去DB也查不到。這樣就會導(dǎo)致,每次根據(jù)這個UserID查用戶信息,都會穿透到數(shù)據(jù)庫,給數(shù)據(jù)庫造成了壓力。為了避免緩存穿透,當(dāng)數(shù)據(jù)庫查不到時,我們可以在緩存中記錄一條空數(shù)據(jù),比如userID做為key,空json做為值,如果程序獲得這個空json,就按用戶不存在處理。再給這個key設(shè)置一個很短的過期時間,比如30秒。

緩存雪崩

我們經(jīng)常會遇到需要初始化緩存的情況。比如說用戶系統(tǒng)重構(gòu),表結(jié)構(gòu)發(fā)生了變化,緩存信息也要變,上線前需要初始化緩存,將用戶信息批量存入緩存。假如我們給這些用戶信息設(shè)置相同的過期時間,到過期時間點所有用戶信息的緩存記錄就會同時集中失效,導(dǎo)致大量請求瞬間打到數(shù)據(jù)庫,數(shù)據(jù)庫很可能會被搞掛。這種緩存集中失效,導(dǎo)致大量請求同時穿透到數(shù)據(jù)庫的情況,就是所謂的“雪崩效應(yīng)”。

所以,當(dāng)我們向緩存初始化數(shù)據(jù)時,要保證每個緩存記錄過期時間的離散性。可以采用一個較大的固定值加上一個較小的隨機值。比如過期時間可以是:10小時 + 0到3600秒的隨機值。

緩存并發(fā)

當(dāng)系統(tǒng)并發(fā)很高,緩存數(shù)據(jù)尤其是熱點數(shù)據(jù)過期后,可能會出現(xiàn)多個請求同時訪問數(shù)據(jù)庫并設(shè)置緩存的情況,不但給數(shù)據(jù)庫帶來壓力,而且會有緩存頻繁更新的問題。

我們可以通過加鎖來避免緩存并發(fā)問題。如果從緩存查不到數(shù)據(jù),對查詢數(shù)據(jù)加分布式鎖,然后查數(shù)據(jù)庫并把數(shù)據(jù)庫查詢結(jié)果放入緩存。其他線程等待鎖釋放后,直接從緩存取值。

比如,電商系統(tǒng)會緩存商品SKU價格,一些熱點商品的并發(fā)訪問會非常高。當(dāng)緩存過期失效后,訪問請求從緩存查不到記錄,此時可以用商品SKU ID為Key加分布式鎖,然后從數(shù)據(jù)庫查詢價格并把價格放入緩存,最后解鎖。解鎖后其他請求就可以從緩存直接取值了。從而避免了數(shù)據(jù)庫的壓力。

分布式鎖

以我們之前做過的5人拼團為例。如果有用戶參加團購,我們需要先校驗參團人數(shù)是否達(dá)到了上限5人。如果沒達(dá)到5人,用戶才可以參團。偽代碼如下:

  1. //根據(jù)拼團ID獲取目前參團成員數(shù)量 
  2. int numOfMembers = pinTuanService.getNumOfMembersById(pinTuanID); 
  3. if(numOfMembers < 5) { 
  4.   pinTuanService.pintuan();//執(zhí)行,加入拼團,生單等邏輯 
  5. }  

高并發(fā)場景下,上面的代碼會有很嚴(yán)重的問題。如果某個團當(dāng)前的參團人數(shù)是4,這時有兩個用戶同時參團,用戶A和用戶B的請求同時進入上面的代碼塊,A和B的請求同時執(zhí)行到第2行代碼,獲取的numOfMembers都是4,表達(dá)式 numOfMembers < 5 成立,所以兩個用戶都能執(zhí)行到第4行代碼,就是說A用戶和B用戶都能成功參加拼團。于是,參團人數(shù)就超過了5人的上限。所以我們就需要加鎖來避免這個問題。synchronized行嗎?不行。因為我們的服務(wù)是多節(jié)點部署的,所以要加分布式鎖。代碼如下:

  1. boolean aquired = distributedLock.aquireLock(pinTuanID, 3000); 
  2. if(aquired == true) { 
  3.   try{ 
  4.     //根據(jù)拼團ID獲取目前參團成員數(shù)量 
  5.     int numOfMembers = pinTuanService.getNumOfMembersById(pinTuanID); 
  6.     if(numOfMembers < 5) { 
  7.       pinTuanService.pintuan();//執(zhí)行,加入拼團,生單等邏輯 
  8.     }  
  9.   } finally { 
  10.     distributedLock.releaseLock(pinTuanID); 
  11.   } 

這樣就好多啦!接下來我們看看基于Redis分布式鎖的實現(xiàn),以及特別要注意的問題。一般我們會基于setnx實現(xiàn)Redis分布式鎖。setnx命令可以檢查key是否存在,如果key不存在,就在Redis中創(chuàng)建一個鍵值對(操作成功),如果key已經(jīng)存在就放棄執(zhí)行(操作失敗)。

先看一段基于Springboot實現(xiàn)的加鎖和釋放鎖的代碼:

  1. @Component 
  2. public class DistributedLock { 
  3.  
  4.  @Autowired 
  5.  private StringRedisTemplate redisTemplate; 
  6.   
  7.  /** 
  8.  * 加鎖 
  9.  * lockKey,redis的key 
  10.  * expireTime,過期時間,單位是毫秒 
  11.  * 注:setIfAbsent方法就使用了redis的setnx 
  12.  */ 
  13.   public boolean aquireLock(String lockKey, long expireTime) { 
  14.    long waitTime = 0; 
  15.    boolean success = redisTemplate.opsForValue().setIfAbsent(lockKey, "distributedLock"
  16.                      expireTime, TimeUnit.MILLISECONDS); 
  17.    if(success == true){ 
  18.       return success;    
  19.    } else { 
  20.      //如果加鎖失敗,循環(huán)重試加鎖 
  21.      while(success != true && waitTime < 5000L ) { 
  22.        success = redisTemplate.opsForValue().setIfAbsent(lockKey, "distributedLock"
  23.                        expireTime, TimeUnit.MILLISECONDS); 
  24.        sleep 100毫秒;                 
  25.        waitTime += 100L; 
  26.      } 
  27.    } 
  28.     
  29.    return success; 
  30.  } 
  31.   
  32.  /** 
  33.  * 釋放鎖 
  34.  * lockKey,redis的key 
  35.  */ 
  36.  public void releaseLock(String lockKey) { 
  37.    redisTemplate.delete(lockKey); 
  38.  }  
  39.   

上面的代碼。乍一看,好像沒什么問題!加鎖失敗有循環(huán)重試加鎖,過期時間設(shè)置了,而且也保證了創(chuàng)建Key-Value鍵值對和設(shè)置過期時間的原子性,這樣當(dāng)程序沒有正常釋放鎖時,也能保證過期后鎖自動釋放(注意:redis較老的版本不支持 setnx 和設(shè)置過期時間的原子操作,不過可以利用Lua腳本來保證原子性)。

我們再仔細(xì)思考一下,一般場景我們會對Key設(shè)置一個很短的過期時間,當(dāng)一次操作因為網(wǎng)絡(luò)等原因耗費了較長時間,操作還沒完成key就過期失效了。這樣會產(chǎn)生什么問題呢?我們還是以拼團為例加以說明,先看看下面這張圖:

 

 

 

 

如上圖,用戶A和用戶B同時參加同一團,團ID為 001,我們以團ID作為分布式鎖的Key,"distributedLock" 作為固定的Value,過期時間是5秒。A先獲取分布式鎖,但是由于網(wǎng)絡(luò)等原因A的拼團操作在5秒內(nèi)沒完成,這時Key過期并從Redis清除掉,A的分布式鎖失效。此時用戶B拿到分布式鎖,Key也同樣是團ID 001。在用戶B的拼團邏輯執(zhí)行完之前,用戶A的邏輯先執(zhí)行完了,緊接著A就把鎖給釋放了。不過A的鎖早已經(jīng)過期失效了,B持有鎖的Key和A又完全一樣,所以此時A釋放的其實是B的鎖。這樣一來整個拼團還是有可能會超員。怎么解決呢?

我們可以把分布式鎖的Value設(shè)成可以區(qū)分的值,比如拼團的場景Value可以設(shè)置為userID,在釋放鎖的時候根據(jù)key和value來判斷當(dāng)前的鎖是不是自己的,只有Redis中userID和自己的userID相同才釋放鎖。

改進后的代碼如下:

  1. @Component 
  2. public class DistributedLock { 
  3.  
  4.  @Autowired 
  5.  private StringRedisTemplate redisTemplate; 
  6.   
  7.  /** 
  8.  * 加鎖 
  9.  * lockKey,redis的key 
  10.  * expireTime,過期時間,單位是毫秒 
  11.  * 注:setIfAbsent方法就使用了redis的setnx 
  12.  */ 
  13.   public boolean aquireLock(String lockKey, String userID, long expireTime) { 
  14.    long waitTime = 0; 
  15.    boolean success = redisTemplate.opsForValue().setIfAbsent(lockKey, userID, 
  16.                      expireTime, TimeUnit.MILLISECONDS); 
  17.    if(success == true){ 
  18.       return success;    
  19.    } else { 
  20.      //如果加鎖失敗,循環(huán)重試加鎖 
  21.      while(success != true && waitTime < 5000L ) { 
  22.        success = redisTemplate.opsForValue().setIfAbsent(lockKey, userID, 
  23.                        expireTime, TimeUnit.MILLISECONDS); 
  24.        sleep 100毫秒;                 
  25.        waitTime += 100L; 
  26.      } 
  27.    } 
  28.     
  29.    return success; 
  30.  } 
  31.   
  32.  /** 
  33.  * 釋放鎖 
  34.  * lockKey,redis的key 
  35.  */ 
  36.  public void releaseLock(String lockKey, String userID) { 
  37.    String userIDFromRedis = redisTemplate.get(lockKey); 
  38.    if( userID.equals(userIDFromRedis) ) { 
  39.      redisTemplate.delete(lockKey); 
  40.    } 
  41.  }  
  42.   

還有一種場景需要考慮。當(dāng)Redis master發(fā)生故障,主備切換時往往會造成數(shù)據(jù)丟失,包括分布式鎖的Key-Value 也可能丟失。這樣就會導(dǎo)致操作還沒執(zhí)行完,鎖就被其他請求拿到了。Redis官方提供了Redlock算法,以及相應(yīng)的開源實現(xiàn) Redisson。用到分布式鎖的場景,大家可以直接使用 Redisson,非常方便。如果系統(tǒng)對可靠性要求很高,如需用到分布式鎖,建議使用 Zookeeper,etcd 等。 

 

 

責(zé)任編輯:龐桂玉 來源: 楊建榮的學(xué)習(xí)筆記
相關(guān)推薦

2020-10-28 11:20:55

vue項目技

2020-11-18 07:52:08

2015-06-29 09:06:51

2022-09-14 08:11:06

分頁模糊查詢

2020-10-08 18:58:46

條件變量開發(fā)線程

2023-01-16 08:09:51

SpringMVC句柄

2015-07-03 11:20:41

編程學(xué)習(xí)方法

2024-02-26 08:19:00

WebSpring容器

2021-08-05 18:21:29

Autowired代碼spring

2016-01-11 09:48:07

2018-05-11 15:53:59

2024-02-19 00:00:00

Console函數(shù)鏈接庫

2017-11-09 15:29:21

CPU溫度常識

2018-03-07 06:37:14

開源項目源代碼云計算

2018-02-06 09:40:25

PythonOOP繼承

2019-02-12 11:15:15

Spring設(shè)計模式Java

2019-07-08 10:18:38

MPLSIP數(shù)據(jù)

2016-03-18 19:03:35

認(rèn)知計算IBM

2018-08-07 09:29:35

數(shù)據(jù)庫MySQL優(yōu)化方法

2021-11-10 15:37:49

Go源碼指令
點贊
收藏

51CTO技術(shù)棧公眾號

久88久久88久久久| 国产欧美一区二区三区在线看蜜臀| 精品香蕉一区二区三区| 久久综合伊人77777麻豆最新章节| 亚洲小说区图片区| 66m—66摸成人免费视频| 爱情岛亚洲播放路线| 中文无字幕一区二区三区| 欧美亚洲国产免费| 国产成人3p视频免费观看| 欧美性猛交xxxx乱大交退制版 | 日产欧产美韩系列久久99| 孩xxxx性bbbb欧美| 免费观看成人性生生活片| 精品视频在线看| 国产网友自拍电影在线| 国产成人无码a区在线观看视频| 久久国产福利国产秒拍| 激情综合网婷婷| 视频二区欧美| 日韩av影片在线观看| 人成在线免费视频| 亚洲免费观看高清| 免费看黄色一级大片| 成人sese在线| 亚洲区成人777777精品| 免费看精品久久片| 日本视频一区二区在线观看| 国产一区久久| 高清视频一区二区三区| 天天综合网91| 92看片淫黄大片欧美看国产片| 自拍偷拍精品| 欧美在线视频导航| 精品淫伦v久久水蜜桃| 久久亚洲精品网站| 久久精品嫩草影院| 久久久999国产精品| av日韩一区| 麻豆国产va免费精品高清在线| 国产亚洲人成a在线v网站| 日韩在线观看免费全集电视剧网站| 性欧美video高清bbw| 欧美变态口味重另类| av在线网址观看| 精品美女一区二区三区| 91超碰免费在线| 亚洲精品99久久久久中文字幕| 午夜羞羞小视频在线观看| 精品国产免费久久 | 黄网视频在线观看| 国产精品丝袜91| 一路向西2在线观看| 国产精品国产三级国产普通话蜜臀 | 91在线九色porny| 国产精品视频白浆免费视频| 美女久久久久| 中文字幕日韩欧美| 久久人人爽爽爽人久久久| www婷婷av久久久影片| 国产综合久久久久久久久久久久 | 亚洲制服国产| 精品国偷自产国产一区| 成人短视频app| 日韩在线观看网址| 国产精品毛片久久久| 高清亚洲成在人网站天堂| 欧洲杯什么时候开赛| 国内成+人亚洲| 国产精品一区2区| 国产又大又黄又猛| 色综合天天综合网国产成人综合天 | 成人免费a级片| 国产网站在线免费观看| 在线免费观看视频一区| 国产传媒在线播放| 日韩电视剧在线观看免费网站| 国产一区二区三区四区五区传媒| 色噜噜狠狠一区二区三区果冻| 成年网站在线| 亚洲男人天堂网| 日本一区福利在线| 91在线网站视频| 精品一区二区国语对白| 9久久婷婷国产综合精品性色| 黄色精品一区二区| 九色porny自拍视频在线播放| 久久久久久网站| 久久久国产精品| 亚洲区成人777777精品| 亚洲免费观看高清完整| 影音先锋男人资源在线| 色综合视频网站| 一本久道综合久久精品| caopor在线视频| 欧美精品一卡两卡| 日本成人手机在线| 精品乱子伦一区二区三区| www国产成人| 成在在线免费视频| 麻豆一区二区在线观看| 在线观看一区| 青青草精品视频在线观看| 91精品麻豆日日躁夜夜躁| 91亚洲无吗| 日韩一区二区三区资源| 亚洲情趣在线观看| 欧美日韩亚洲国产| 国产伦精品一区二区三区视频黑人| av成人老司机| 在线中文字幕第一页| 庆余年2免费日韩剧观看大牛| 日本成人超碰在线观看| 黄动漫在线免费观看| xvideos亚洲人网站| 国产精品免费看| 色琪琪免费视频网站| 伊人伊成久久人综合网小说 | 精品成人一区二区三区| 欧美日韩有码| 成人午夜精品久久久久久久蜜臀| 国产精品欧美一区二区三区不卡 | 欧美日韩福利在线观看| 亚洲电影成人| 人妻丰满熟妇av无码区app| 日韩视频一区二区在线观看| 欧美久久综合网| 黑森林福利视频导航| 亚洲人成伊人成综合网久久久 | 97在线精品| 波多野结衣xxxx| 中文字幕免费国产精品| 欧美aⅴ一区二区三区视频| 久久国产精品高清一区二区三区| 欧美精品www在线观看| 国产.欧美.日韩| 看黄在线观看| 久久伦理网站| 色婷婷久久一区二区三区麻豆| 国产乱论精品| 欧美 国产 综合| 亚洲精品狠狠操| 玖玖精品视频| 麻豆视频在线观看免费网站| 91在线中文字幕| 亚洲成人精品影院| 欧美热在线视频精品999| 日本成人黄色网| 久久天堂电影网| 国产jizzjizz一区二区| 黑人巨大精品| 日韩精品久久一区二区| 亚洲人成在线观| 国产69精品久久777的优势| 国产成人精品123区免费视频| 亚洲一区二区三区精品动漫| 91精品国产综合久久福利 | 免费av片风间由美在线| 91国产精品电影| 亚洲三级在线看| 日韩理论电影| 日韩国产福利| 国产欧美一区二区视频| 91麻豆精品国产无毒不卡在线观看| 亚洲先锋成人| 成人女同在线观看| 蜜臀av性久久久久蜜臀av| 中文字幕免费精品一区| 久久免费精品国产久精品久久久久| 日韩视频在线你懂得| 国产精品全国免费观看高清| 一本色道久久综合亚洲aⅴ蜜桃| 91gao视频| 国产精品一区二区三区视频网站| 亚洲午夜一级| 在线观看91视频| 国产欧美日韩三区| 女人让男人操自己视频在线观看| 黄色成人免费观看| 欧美女人性生活视频| 国产男女无遮挡| 24小时免费看片在线观看| 精品美女一区| 国产精品久久精品国产| 97se亚洲国产综合自在线不卡| 美女露隐私免费网站| 欧美日韩亚洲免费| 97国产真实伦对白精彩视频8| 欧美性xxxxx极品娇小| 成人午夜电影久久影院| 欧美私人啪啪vps| 精品亚洲自拍| 免费亚色电影在线观看| 欧美 日韩 国产 激情| 成人久久一区二区| 欧美一区二区久久| 欧美国产精品一区二区| 天天综合网 天天综合色| 欧美国产欧美亚州国产日韩mv天天看完整| 亚洲美女啪啪|