文章很長,且持續更新,建議收藏起來,慢慢讀!瘋狂創客圈總目錄 博客園版 爲您奉上珍貴的學習資源 :
免費贈送 :《尼恩Java面試寶典》 持續更新+ 史上最全 + 面試必備 2000頁+ 面試必備 + 大廠必備 +漲薪必備
免費贈送 :《尼恩技術聖經+高併發系列PDF》 ,幫你 實現技術自由,完成職業升級, 薪酬猛漲!加尼恩免費領
免費贈送 經典圖書:《Java高併發核心編程(卷1)加強版》 面試必備 + 大廠必備 +漲薪必備 加尼恩免費領
免費贈送 經典圖書:《Java高併發核心編程(卷2)加強版》 面試必備 + 大廠必備 +漲薪必備 加尼恩免費領
免費贈送 經典圖書:《Java高併發核心編程(卷3)加強版》 面試必備 + 大廠必備 +漲薪必備 加尼恩免費領
免費贈送 資源寶庫: Java 必備 百度網盤資源大合集 價值>10000元 加尼恩領取
絕命一問:秒殺Redis分段鎖,如何設計?
尼恩說在前面
秒殺,是一個非常常見的高併發面試題,很多面試官也非常熟悉,上來就讓面試者設計一個秒殺系統。
在40歲老架構師 尼恩的讀者交流羣(50+)中,最近有小夥伴拿到了一線互聯網企業如得物、阿里、滴滴、極兔、有贊、shein 希音、百度、網易的面試資格,遇到很多很重要的面試題:
分佈式鎖,如何實現高併發?
小夥伴 沒有回答好,導致面試掛了。
小夥伴面試完了之後,來求助尼恩:如何才能回答得很漂亮,才能 讓面試官刮目相看、口水直流。所以,尼恩給大家做一下系統化、體系化的梳理,使得大家內力猛增,可以充分展示一下大家雄厚的 “技術肌肉”,讓面試官愛到 “不能自已、口水直流”,然後實現 ”offer自由”。
當然,這道面試題,以及參考答案,也會收入咱們的 《尼恩Java面試寶典》V173版本PDF集羣,供後面的小夥伴參考,提升大家的 3高 架構、設計、開發水平。
注:本文以 PDF 持續更新,最新尼恩 架構筆記、面試題 的PDF文件,請到公衆號【技術自由圈】獲取。
本文目錄
問題場景:熱點庫存扣減問題
秒殺場景,有一個難度的問題:熱點庫存扣減問題。
-
既要保證不發生超賣
-
又要保證高併發
如何解決這個高難度的問題呢? 答案就是使用redis 分段鎖。
首先說說什麼是分佈式鎖,普通分佈式鎖的不足
比如說在一個分佈式系統中,存在客戶端多個用戶,同時通過多個業務微服務,發起一個數據修改。
如果沒有分佈式鎖機制保證,在那多臺機器上的多個服務可能進行併發修改操作,導致數據修改的不一致,出現髒讀髒寫,這就會造成問題。
而分佈式鎖機制就是爲了解決類似這類問題,保證多個服務之間互斥的訪問共享資源,如果一個服務搶佔了分佈式鎖,其他服務沒獲取到鎖,就不進行後續操作。
上圖中,客戶端1的服務搶佔了分佈式鎖,可以去扣減庫存。
其他服務沒獲取到分佈式鎖,就不進行後續操作。
什麼是分佈式鎖?
- 當在分佈式模型下,數據只有一份(或有限制),此時需要利用鎖的技術控制某一時刻修改數據的進程數。
- 用一個狀態值表示鎖,對鎖的佔用和釋放通過狀態值來標識。
分佈式鎖的條件:
- 互斥性。在任意時刻,只有一個客戶端能持有鎖。
- 不會發生死鎖。即使有一個客戶端在持有鎖的期間崩潰而沒有主動解鎖,也能保證後續其他客戶端能加鎖。
- 具有容錯性。只要大部分的 Redis 節點正常運行,客戶端就可以加鎖和解鎖。
- 解鈴還須繫鈴人。加鎖和解鎖必須是同一個客戶端,客戶端自己不能把別人加的鎖給解了。
普通的分佈式鎖的如何實現?
分佈式鎖的實現由很多種,文件鎖、數據庫、redis等等,比較多;分佈式鎖常見的多種實現方式:
- 數據庫悲觀鎖、
- 數據庫樂觀鎖;
- 基於Redis的分佈式鎖;
- 基於ZooKeeper的分佈式鎖。
在實踐中,還是redis做分佈式鎖性能會高一些
尼恩提示:基於ZooKeeper的分佈式鎖,請參見尼恩面試寶典 專題15.
尼恩提示:基於redis 的分佈式鎖,請參見尼恩面試寶典 專題15.
普通分佈式鎖的性能問題
分佈式鎖一旦加了之後,對同一個商品的下單請求,會導致所有下單操作,都必須對同一個商品key加分佈式鎖。
假設某個場景,一個商品1分鐘6000訂單,每秒的 600個下單操作,
假設加鎖之後,釋放鎖之前,查庫存 -> 創建訂單 -> 扣減庫存,每個IO操作100ms,大概300毫秒。
具體如下圖:
可以再進行一下優化,將 創建訂單 + 扣減庫存 併發執行,將兩個100ms 減少爲一個100ms,這既是空間換時間的思想,大概200毫秒。
將 創建訂單 + 扣減庫存 批量執行,減少一次IO,也是大概200毫秒。也就是單個商品而言,只有 5 QPS.
假設一個商品sku的數量是10000,10秒內秒殺完,也就是單個商品而言,需要 單商品 100 QPS,如何應對一個商品的 100qps秒殺。
甚至,如果單商品需要 1000qps秒殺呢?
答案是,使用 分段加鎖。
分段加鎖的思想來源
分段加鎖的思想來源與基礎知識。在尼恩的 《Java 高併發核心編程 卷2》 介紹了 JUC的 LongAdder 和 ConcurrentHashMap的源碼和底層原理,他們提升性能的辦法是: 分段加鎖,空間換時間
尼恩經常在技術自由圈社羣裏邊,對小夥伴們強調 基礎知識的重要性,反覆強調, 《Java 高併發三部曲》 一定要多刷,最好刷三遍。
尤其是 LongAdder 的實現思想,可以用於 Redis分佈式鎖 作爲性能提升的參考設計方案,將 Redis分佈式鎖 優化爲 Redis分段鎖。
建議大家提前看看LongAdder ,有關LongAdder 的系統化學習,請參見 《Java 高併發核心編程 卷2》
優化之後:使用Redis分段鎖提升秒殺的併發性能
回到前面的場景:
假設一個商品1分鐘6000訂單,每秒的 600個下單操作,
假設加鎖之後,釋放鎖之前,查庫存 -> 創建訂單 -> 扣減庫存,經過優化,每個IO操作100ms,大概200毫秒,一秒鐘5個訂單。
如何提高性能呢? 空間換時間
爲了達到每秒600個訂單,可以將鎖分成 600 /5 =120 個段,反過來, 每個段1秒可以操作5次, 120個段,合起來,及時每秒操作600次。
進行搶奪鎖的,如果申請到一個具體的段呢?
- 隨機路由法
- hash取模法
如果是用隨機路由算法,可以將請求隨機到一個分段, 如果不行,就輪詢下一個分段,具體的流程,大致如下:
這個是一個理論的時間預估,沒有扣除 嘗試下一個分段的 時間, 另外,實際上的性能, 會比理論上差,從咱們實操案例的測試結果,也可以證明這點。
隨機路由法的問題:
不同分端之間,可能庫存消耗不均,導致部分用戶無法扣減庫存,反覆進行重試,拖慢系統性能。
如何進一步優化: hash取模法。
第二次優化之後:使用hash取模法,減少庫存消耗不均和無效重試
由於秒殺場景的分佈式鎖,實際上是爲了防止超賣, 和庫存是強相關的。
所以,可以結合庫存,把秒殺的分佈式鎖進行改進。
第一步: 把redis 的分段方式進行演進,額外增加一個總庫存分段鎖,用於分配存儲剩餘的總庫存。採用多批次少量分配的思路,通過定時任務,從總庫存向分段庫存中遷移庫存。
第二步:使用hash取模法,把用戶路由到某一個分段,如果分段裏邊的庫存耗光了,就去訪問剩餘的總庫存。
庫存動態遷移
爲了防止分段多庫存耗光,大家都去搶佔總庫存鎖。
採用多批次少量分配的思路,通過定時任務,從總庫存向分段庫存中遷移庫存。
至此, hash取模法的分段鎖設計方案,已經完美實現。
並且尼恩社羣中,已經有小夥伴在生產上完成落地。 以上方案,也是尼恩在給他一對一改簡歷的時候,分享給尼恩的。
當然,如果大家簡歷挖掘不出來亮點,也可以找尼恩挖掘, 保證簡歷金光閃閃、改天換地。
說在最後
秒殺相關的面試題,是非常常見的面試題。
以上的內容,如果大家能對答如流,如數家珍,基本上 面試官會被你 震驚到、吸引到。最終,讓面試官愛到 “不能自已、口水直流”。offer, 也就來了。
在面試之前,建議大家系統化的刷一波 5000頁《尼恩Java面試寶典》V173,在刷題過程中,如果有啥問題,大家可以來 找 40歲老架構師尼恩交流。
另外,如果沒有面試機會,可以找尼恩來幫扶、領路。
尼恩已經指導了大量的就業困難的小夥伴上岸,前段時間,幫助一個40歲+就業困難小夥伴拿到了一個年薪100W的offer,小夥伴實現了 逆天改命 。
技術自由的實現路徑:
實現你的 架構自由:
《阿里二面:千萬級、億級數據,如何性能優化? 教科書級 答案來了》
《峯值21WQps、億級DAU,小遊戲《羊了個羊》是怎麼架構的?》
… 更多架構文章,正在添加中
實現你的 響應式 自由:
這是老版本 《Flux、Mono、Reactor 實戰(史上最全)》
實現你的 spring cloud 自由:
《Spring cloud Alibaba 學習聖經》 PDF
《分庫分表 Sharding-JDBC 底層原理、核心實戰(史上最全)》
《一文搞定:SpringBoot、SLF4j、Log4j、Logback、Netty之間混亂關係(史上最全)》
實現你的 linux 自由:
實現你的 網絡 自由:
《網絡三張表:ARP表, MAC表, 路由表,實現你的網絡自由!!》
實現你的 分佈式鎖 自由:
實現你的 王者組件 自由:
《隊列之王: Disruptor 原理、架構、源碼 一文穿透》
《緩存之王:Caffeine 源碼、架構、原理(史上最全,10W字 超級長文)》
《Java Agent 探針、字節碼增強 ByteBuddy(史上最全)》