一張優惠券引發血案

      網友投稿 824 2022-05-30

      整個優惠券中心分為前端和后端,小灰所負責的是后端RPC接口的開發。接口中包含“查券”和“領券”兩個方法,項目大體結構如下圖:

      小灰原本的優惠券查詢接口是這樣實現的:

      優惠券列表在Redis中以List的形式存儲,查詢時的邏輯很簡單:

      1.查詢緩存,如果緩存存在,返回結果

      2.緩存不存在,查詢數據庫

      3.把查詢數據庫的結果循環放入緩存

      然而,當某個時間點緩存不存在,請求量又很大的時候,會出現緩存并發的問題。也就是多個線程會重復去查詢DB,又重復去更新緩存。(注意,這并不是緩存擊穿,很多人在這兩個概念上混淆。)

      這其中重復查詢DB是次要問題,而重復更新緩存則是主要問題。假如有兩個線程同時進入上述的第三個階段,各自進行rpush操作,那么最終會在優惠券列表的緩存中插入兩組同樣的數據。

      怎么解決呢?用Java的鎖機制?顯然不行,因為線上環境通常都是多個服務器組成的集群。于是小灰想到了利用分布式鎖。

      所謂分布式鎖有很多種,可以利用ZooKeeper、MemCache、Redis來實現。其中Redis的方式比較簡單,無非是利用一個服務器之間共享的Key,以及Setnx指令。

      當第一個線程執行Setnx,會存儲對應的鍵值,相當于成功獲得鎖。當后續再有線程對同于的Key執行Setnx指令,則會返回空,相當于搶鎖失敗。同時,為了防止一個線程因意外情況而長久把持著鎖,程序對Key設置了1秒的過期時間。

      歸納一下修改后的邏輯:

      1.查詢緩存,如果緩存存在,返回結果

      2.緩存不存在,查詢數據庫

      3.爭奪分布式鎖

      4.成功獲得鎖,把查詢數據庫的結果循環放入緩存

      5.釋放分布式鎖

      詭異的bug又重現了,因為小灰上次的改動仍然存在一個致命的漏洞。在這里我們假定緩存不存在,剛好有兩個線程A和B一后一先進入到代碼塊。

      第一階段,線程A剛開始查詢優惠券緩存,線程B正嘗試獲取分布式鎖:

      一張優惠券引發的血案

      第二階段,由于緩存不存在,線程A開始查詢數據庫,線程B成功獲得鎖,開始更新緩存:

      第三階段,線程A嘗試獲得分布式鎖,而線程B已經釋放分布式鎖:

      第四階段,線程A獲得了鎖,又一次更新緩存,而線程B已經成功返回:

      就這樣,緩存被重復更新了兩次,所以再次出現數據重復的bug。

      這種局面如何破解呢?其實不難,只需在線程成功得到鎖以后,再次判斷優惠券緩存的存在:

      歸納一下修改后的邏輯:

      1.查詢緩存,如果緩存存在,返回結果

      2.緩存不存在,查詢數據庫

      3.爭奪分布式鎖

      4.成功獲得鎖,再次判斷緩存的存在

      5.如果緩存仍舊不存在,把查詢數據庫的結果循環放入緩存

      6.釋放分布式鎖

      這種二次判斷存在性的機制有一個專門的名字,叫做雙重檢測。該方法在線程安全的單例模式中也常常被用到。

      幾點補充:

      1.文中所使用的分布式鎖,其實并不是“正宗”的分布式鎖,當線程爭奪鎖失敗的時候,會直接返回查詢DB的結果,而不會依靠自旋機制來等鎖。

      2.為什么優惠券列表的信息要使用List類型來存入緩存,而不是把整個列表存為一個很長的Json字符串?這是由于業務需要,使用List在某些情況下更方便對單個優惠券信息進行修改(LSET指令)。

      3.為什么優惠券列表的信息不使用Redis的Set或者Hash數據類型來存儲,實現自動去重呢?對于Set類型,去重前需要對比整個字符串是否完全相同,而每一張優惠券是一個較長的Json字符串,對比的效率會比較低。使用Hash倒是可以實現高效的去重,但并未在根本上解決重復更新的問題。

      —————END—————

      喜歡本文的朋友們,歡迎長按下圖關注訂閱號夢見,收看更多精彩內容

      Redis 緩存

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:和上海電力的朋友一起聊聊數字化轉型
      下一篇:云計算的技術特點
      相關文章
      亚洲第一网站男人都懂| 亚洲精品国产精品乱码不卞 | 亚洲色大成网站WWW久久九九| 亚洲AV无码XXX麻豆艾秋| 亚洲熟女www一区二区三区| 亚洲最大福利视频| 亚洲 欧洲 日韩 综合在线| 亚洲人成网站在线观看播放动漫 | 亚洲激情黄色小说| 亚洲图片在线观看| 亚洲综合激情九月婷婷| 亚洲特级aaaaaa毛片| 亚洲一卡2卡4卡5卡6卡在线99| 91亚洲国产成人久久精品网址| 亚洲香蕉在线观看| 亚洲最大的成人网站| 亚洲精品美女久久7777777| 亚洲爆乳成av人在线视菜奈实| 久久亚洲精品无码网站| 国产亚洲精彩视频| 亚洲区小说区图片区| 亚洲愉拍99热成人精品热久久| 日本亚洲视频在线 | 久久精品九九亚洲精品天堂| 日本亚洲视频在线| 精品亚洲成AV人在线观看| 亚洲啪啪免费视频| jiz zz在亚洲| 久久久久亚洲精品无码网址色欲| 亚洲第一页综合图片自拍| 亚洲中文字幕日产乱码高清app| 亚洲精品乱码久久久久久按摩 | 亚洲av日韩综合一区久热| 亚洲不卡AV影片在线播放| 久久精品国产精品亚洲下载| 国产精品亚洲一区二区三区在线 | 亚洲国产精品国自产拍电影| 亚洲成人免费电影| 亚洲欧洲国产综合AV无码久久| 四虎亚洲国产成人久久精品| 亚洲日韩精品一区二区三区无码|