Redis分布式鎖實戰:從入門到生產級方案

目錄

一、為什么需要分布式鎖?

二、Redis分布式鎖核心特性

三、實現方案與代碼詳解

方案1:基礎版 SETNX + EXPIRE

原理

代碼示例

問題

方案2:Redisson框架(生產推薦)

核心特性

代碼示例

優勢

方案3:RedLock算法(Redis集群)

適用場景

實現步驟

代碼片段

方案4:Lua腳本原子化操作

解決基礎版問題

Lua腳本示例

Java調用方式

四、高級場景與解決方案

場景1:公平鎖(按順序獲取)

實現思路

代碼邏輯

場景2:可重入鎖

實現機制

Redisson實現

五、避坑指南與最佳實踐

1. 鎖誤刪問題

2. 鎖續期問題

3. 主從一致性問題

4. 性能優化

六、總結與選型建議


一、為什么需要分布式鎖?

在微服務、多機部署場景中,多個進程可能同時競爭同一資源(如庫存、訂單)。傳統JVM鎖僅作用于本地,無法保證分布式環境的互斥訪問。例如:

  • 電商庫存超賣:兩個服務實例同時查詢庫存>0,均執行扣減操作。
  • 定時任務重疊:多節點觸發相同任務(如對賬),導致數據混亂。

Redis憑借高性能、原子操作和豐富數據結構,成為分布式鎖的首選方案。

二、Redis分布式鎖核心特性

  1. 互斥性:僅一個客戶端能持有鎖
  2. 防死鎖:鎖需自動過期(如SET EX)
  3. 容錯性:節點故障不影響鎖機制
  4. 可重入性(可選):同一線程可多次加鎖
  5. 公平性(可選):按申請順序分配鎖

三、實現方案與代碼詳解

方案1:基礎版 SETNX + EXPIRE

原理
  • SET key value NX EX seconds:原子設置鍵值并設置過期時間
  • 若返回?true?則獲取鎖,否則重試
代碼示例
String lockKey = "product_stock_lock";
String lockValue = UUID.randomUUID().toString(); // 唯一標識// 嘗試加鎖
Boolean success = jedis.set(lockKey, lockValue, "NX", "EX", 30);
if (success != null && success) {// 獲取鎖成功,執行業務邏輯try {// 扣減庫存操作} finally {// 釋放鎖if (lockValue.equals(jedis.get(lockKey))) {jedis.del(lockKey);}}
} else {// 獲取鎖失敗,重試或返回
}
問題
  • 鎖誤刪:業務未完成時鎖過期,其他線程可能刪除當前線程的鎖
  • 非原子操作set?和?get?存在競態條件

方案2:Redisson框架(生產推薦)

核心特性
  • 可重入鎖:同一線程可多次加鎖,計數器管理
  • 看門狗機制:默認每10秒續期鎖至30秒,防止業務超時
  • 異步兼容:支持?tryLock()?阻塞等待和?isLocked()?狀態檢查
代碼示例
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redisson = Redisson.create(config);RLock lock = redisson.getLock("order_lock");
try {// 嘗試加鎖(最多等待10秒,鎖過期30秒)if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {// 處理訂單邏輯} else {log.warn("獲取鎖失敗");}
} catch (InterruptedException e) {Thread.currentThread().interrupt();
} finally {lock.unlock(); // 必須在finally中釋放
}
優勢
  • 自動處理鎖續期、可重入、跨進程兼容
  • 支持主從切換(通過Redis主從復制)

方案3:RedLock算法(Redis集群)

適用場景
  • Redis集群環境(至少3個獨立節點)
  • 需容忍半數節點故障仍保持鎖可用
實現步驟
  1. 向多數節點(N/2+1)發送加鎖請求
  2. 所有節點設置相同鍵值和過期時間
  3. 成功加鎖后,鎖有效時間為?T - 網絡延遲
代碼片段
// 配置多個Redisson客戶端連接不同節點
Config config1 = new Config();
config1.useSingleServer().setAddress("redis://node1:6379");
Config config2 = new Config();
config2.useSingleServer().setAddress("redis://node2:6380");
// 創建RedLock對象
RedissonRedLock redLock = new RedissonRedLock(Redisson.create(config1),Redisson.create(config2)
);// 加鎖邏輯
try {boolean locked = redLock.tryLock(10, 30, TimeUnit.SECONDS);if (locked) {// 業務邏輯}
} finally {redLock.unlock();
}

方案4:Lua腳本原子化操作

解決基礎版問題
  • 原子驗證+刪除:通過Lua腳本確保操作原子性
  • 唯一標識:用UUID區分鎖歸屬
Lua腳本示例
-- 釋放鎖腳本(判斷鍵值是否匹配)
if redis.call('get', KEYS[1]) == ARGV[1] thenreturn redis.call('del', KEYS[1])
elsereturn 0
end
Java調用方式
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +"return redis.call('del', KEYS[1]) else return 0 end";
Object result = jedis.eval(script, 1, lockKey, lockValue);

四、高級場景與解決方案

場景1:公平鎖(按順序獲取)

實現思路
  • 使用?List?存儲等待隊列
  • 通過?ZSet?記錄申請時間戳,優先分配最早請求
代碼邏輯
// 申請鎖時加入隊列
jedis.lpush("lock_queue", clientId);
jedis.zadd("lock_timestamp", System.currentTimeMillis(), clientId);// 檢查隊列頭部是否是自己
String head = jedis.lrange("lock_queue", 0, 0).get(0);
if (head.equals(clientId) && currentTimeMatch(jedis.zscore("lock_timestamp", head))) {// 獲取鎖成功
}

場景2:可重入鎖

實現機制
  • 使用?Hash?存儲線程ID和重入次數
  • 加鎖時遞增計數器,解鎖時遞減(歸零后刪除鎖)
Redisson實現
RLock lock = redisson.getLock("reentrant_lock");
lock.lock(); // 第一次加鎖
// 嵌套調用同一鎖
lock.lock(); // 重入計數+1
...
lock.unlock(); // 計數-1,歸零后刪除鎖

五、避坑指南與最佳實踐

1. 鎖誤刪問題

  • 原因:鎖過期后,其他線程刪除了當前線程的鎖
  • 解決方案
    • 使用唯一標識(如UUID+線程ID)作為鎖值
    • 釋放鎖前通過Lua腳本校驗歸屬

2. 鎖續期問題

  • 看門狗機制:Redisson自動續期,業務長時間運行時需顯式指定超時時間
  • 手動續期:調用?lock.expire(seconds)?延長鎖時間

3. 主從一致性問題

  • 癥狀:主節點寫鎖后宕機,從節點未同步鎖信息
  • 解決方案
    • 使用RedLock算法(需多數節點加鎖成功)
    • 開啟Redis主從復制的WAIT命令(Redis 7+)

4. 性能優化

  • 減少鎖粒度:按業務ID(如訂單號)細化鎖范圍
  • 避免嵌套鎖:同一線程內多次加鎖需謹慎處理重入
  • 監控指標:鎖獲取成功率、平均等待時間、超時次數

六、總結與選型建議

方案適用場景優點缺點
SETNX + EXPIRE快速原型、單節點場景簡單高效需手動處理細節
RedissonJava項目、生產環境功能完善,自動續期依賴第三方庫
RedLockRedis集群、高容錯需求強一致性,容錯性強性能較低,實現復雜
Lua腳本需嚴格原子操作的場景徹底解決非原子問題需維護腳本
發布/訂閱高并發減少輪詢節省資源消息可靠性需保障

最佳實踐

  • 優先使用Redisson框架(生產環境)
  • 單節點快速實現可選SETNX+Lua腳本
  • 集群環境采用RedLock算法
  • 鎖名稱統一規范(如resource_type:id
  • 超時時間設為業務平均耗時的2倍

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/87329.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/87329.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/87329.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Redis】StringRedisTemplate 和 RedisTemplate 的區別

StringRedisTemplate 和 RedisTemplate 是 Spring Data Redis 提供的兩種用于操作 Redis 的模板類,它們的核心區別在于 序列化方式 和 操作的數據類型。以下是兩者的主要區別和使用建議: ? 1. 數據類型支持 類名支持的數據類型說明RedisTemplate支持所…

docker-compose快速搭建redis集群

目錄結構 redis-cluster/ ├── config/ │ ├── master.conf │ ├── slave1.conf │ └── slave2.conf └── docker-compose.yml配置文件內容 1. config/master.conf # Redis主節點配置 port 6379 bind 0.0.0.0 protected-mode no logfile "redis-mas…

SpringCloud系列(39)--SpringCloud Gateway常用的Route Predicate

前言:在上一節中我們實現了SpringCloud Gateway的動態路由 ,而在本節中我們將著重介紹各種Route Predicate的作用。 1、可以到官方文檔里查看常用的Route Predicate的種類 https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.REL…

漸變色的進度條控件

近日,用VB.net2003重寫了一個漸變色的進度條控件。主要有以下功能: 支持自定義進度條分段數量,可拆分為多個步驟;每個步驟可獨立顯示完成百分比及漸變色效果。 每個步驟均可配置任務名稱和描述;運行時能實時顯示當前執…

【DICOM后處理】qt+vs 實現DICOM數據四視圖顯示

目錄 1、DICOM四視圖2、vtkImageViewer2 實現二維平面圖顯示3、vtkVolume實現三維體數據顯示4、實現界面圖 1、DICOM四視圖 DICOM四視圖通常指同時顯示醫學影像的四個不同平面或視角,用于全面分析三維數據(如CT、MRI等)。 標準四視圖布局&a…

Google Maps 安裝使用教程

一、Google Maps 簡介 Google Maps 是谷歌提供的地圖服務,通過其 JavaScript API,開發者可以在網頁中嵌入地圖,添加標記、路徑、地理編碼、路線導航等功能,適用于位置展示、物流追蹤、LBS 應用等場景。 二、獲取 Google Maps API…

Nginx+Keepalived實現前臺服務高可用

現階段項目開發往往采用前后臺分離,前臺常用的技術有vue、react等,前臺代碼部署在nginx中,代碼中配置了后臺服務的網關地址,由網關向后臺分發服務請求,架構示意圖如下: 在上述架構圖中,如果Ngin…

Gradio全解13——MCP協議詳解(5)——Python包命令:uv與uvx實戰

Gradio全解13——MCP協議詳解(5)——Python包命令:uv與uvx實戰 第13章 MCP協議詳解13.5 Python包命令:uv與uvx實戰13.5.1 uv核心亮點與常用命令1. uv介紹2. 安裝與項目管理3. 腳本與工具4. Python版本與pip接口 13.5.2 uv核心指令…

OD 算法題 B卷【求最小步數】

文章目錄 求最小步數 求最小步數 求從坐標零點到坐標點n的最小步數,一次只能沿著橫坐標軸向左或向右移動2或3;途經的坐標點可以為負數; 輸入描述: 坐標點n 輸出描述: 從坐標零點移動到坐標點n的最小步數 n在【1,10^9】 示例1 輸入&#xf…

Elasticsearch 集群升級實戰指引—7.x 升級到 8.x

升級Elasticsearch集群從7.x到8.x是一項復雜且關鍵的任務,涉及重大版本變更(如API調整、配置變更、安全功能強制啟用等),可能影響集群的性能和穩定性。結合您提到的業務量增長導致索引寫入變慢的問題,本指引不僅提供詳…

JWT學習總結

文章目錄 前置知識Authorization頭部和 CookieCRSF攻擊 JWT概念JWT認證流程使用Springboot整合JWTJwtUtil JWT案例控制器JWT攔截器注冊攔截器結果 session VS Jwt 前置知識 Authorization頭部和 Cookie Authorization 頭部和 Cookie 是 HTTP 協議中兩種不同的身份認證 / 信息…

阿里云消息隊列 Apache RocketMQ 創新論文入選頂會 ACM FSE 2025

近日,由阿里云消息團隊發表的 Apache RocketMQ 創新論文被 CCF-A 類軟件工程頂級會議 FSE 2025 Industry Track 錄用。 ACM FSE(The ACM International Conference on the Foundations of Software Engineering)是享有盛譽的國際學術會議&…

定制WordPress管理后臺

WordPress作為全球最流行的建站工具,因其靈活性和易用性受到廣泛歡迎。許多服務器提供商都支持一鍵安裝WordPress,例如Hostease,使新手用戶也能輕松搭建屬于自己的網站。然而,后臺的默認設置可能無法完全滿足不同用戶的需求。定制…

REST API設計與Swagger:構建高效、易用的Web服務

引言 在現代Web開發中,REST API已成為不同系統間通信的標準方式。一個設計良好的REST API不僅能提高開發效率,還能改善用戶體驗。而Swagger(現稱為OpenAPI)作為API文檔和測試的強大工具,已經成為API開發中不可或缺的一…

一個非對齊訪問的問題

1、引言 最近在編寫代碼時,出現了這樣一個 bug。程序一跑,系統就崩潰了,報錯是 bus error。 目標平臺:ARM32 最終定位到出錯的代碼片段: *((uint32_t *)ptr) id;這里的 ptr 是一個非 4 字節對齊的地址!&a…

【構造】P8976 「DTOI-4」排列|普及+

本文涉及知識點 構造 P8976 「DTOI-4」排列 題目背景 Update on 2023.2.1:新增一組針對 yuanjiabao 的 Hack 數據,放置于 #21。 Update on 2023.2.2:新增一組針對 CourtesyWei 和 bizhidaojiaosha 的 Hack 數據,放置于 #22。…

多路I/O轉接服務器(select、poll、epoll)

多路IO轉接服務器也叫做多任務IO服務器。該類服務器實現的主旨思想是,不再由應用程序自己監視客戶端連接,取而代之由內核替應用程序監視文件。 IO 多路轉接方式比較: 常見的 IO 多路轉接方式有:select、poll、epoll,他…

最新臨時文件快傳系統源碼 輕量化 帶后臺

簡介: 最新臨時文件快傳系統源碼 輕量化 帶后臺 首發 輕松上傳文件并生成提取碼分享給他人,無需注冊,方便快捷。 圖片:

MyBatis多數據源動態連接工具類實現

這個DatabaseService工具類提供了動態創建MyBatis SqlSession的能力,可以靈活地連接到不同的數據庫,非常適合需要動態切換數據源的場景。 package com.cmes.immp.device.utils;import lombok.SneakyThrows; import org.apache.commons.dbcp2.BasicDataS…

用亮數據 MCP 驅動 Trae 智能體:打造高效亞馬遜商品采集與分析助手

本文適合希望快速構建數據驅動型智能體的開發者、數據工程師及 AI 產品設計者閱讀 并非廣告,希望本文可以幫助有需求的同學,祝大家天天開心 在數字時代,數據是決策與洞察趨勢的關鍵。但移動互聯網數據獲取不易,傳統爬蟲技術面對復…