Redis使用SETNX命令實(shí)現(xiàn)分布式鎖
什么是分布式鎖
分布式鎖是一種用于在分布式系統(tǒng)中控制多個(gè)節(jié)點(diǎn)對(duì)共享資源進(jìn)行訪問的機(jī)制。在分布式系統(tǒng)中,由于多個(gè)節(jié)點(diǎn)可能同時(shí)訪問和修改同一個(gè)資源,因此需要一種方法來確保在任意時(shí)刻只有一個(gè)節(jié)點(diǎn)能夠?qū)Y源進(jìn)行操作,以避免數(shù)據(jù)不一致或沖突。分布式鎖就是用來實(shí)現(xiàn)這種互斥訪問的工具。
為什么 Redis 的 SETNX 可以實(shí)現(xiàn)分布式鎖
Redis 的 SETNX 命令(即 SET if Not eXists)可以用來實(shí)現(xiàn)分布式鎖,原因如下:
- 原子性:
SETNX是一個(gè)原子操作,這意味著在同一時(shí)間只有一個(gè)客戶端能夠成功設(shè)置鍵值對(duì)。如果鍵已經(jīng)存在,SETNX將不會(huì)執(zhí)行任何操作。這種原子性確保了鎖的獲取和釋放是線程安全的。 - 唯一性:
SETNX確保了鎖的唯一性。只有第一個(gè)嘗試設(shè)置鍵的客戶端能夠成功,其他客戶端在嘗試設(shè)置相同的鍵時(shí)會(huì)失敗。這模擬了鎖的“獲取”和“釋放”行為。 - 過期時(shí)間:通過結(jié)合
EXPIRE命令或使用SET命令的EX選項(xiàng),可以為鎖設(shè)置一個(gè)過期時(shí)間。這防止了鎖被永久占用,即使客戶端在持有鎖期間崩潰或未能正確釋放鎖。 - 分布式環(huán)境:Redis 是一個(gè)分布式內(nèi)存數(shù)據(jù)庫(kù),可以在多個(gè)節(jié)點(diǎn)之間共享數(shù)據(jù)。因此,使用 Redis 實(shí)現(xiàn)的鎖可以在分布式系統(tǒng)中的多個(gè)節(jié)點(diǎn)之間共享,從而實(shí)現(xiàn)分布式鎖。
- 高性能:Redis 是一個(gè)高性能的數(shù)據(jù)庫(kù),能夠處理大量的并發(fā)請(qǐng)求。這使得 Redis 非常適合作為分布式鎖的實(shí)現(xiàn)基礎(chǔ)。
準(zhǔn)備工作
創(chuàng)建一個(gè)Spring Boot項(xiàng)目,并引入相關(guān)依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
在application.yml文件中配置 Redis 連接信息:
server:
port: 8080
spring:
redis:
host: xxx.xxx.xxx.xxx
port: 6379
password: xxxxxx
具體實(shí)現(xiàn)
創(chuàng)建分布式鎖工具類
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
public class DistributedLock {
private final StringRedisTemplate redisTemplate;
// 通過構(gòu)造函數(shù)注入 StringRedisTemplate
public DistributedLock(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
/**
* 嘗試獲取分布式鎖
*
* @param lockKey 鎖的鍵
* @param requestId 請(qǐng)求標(biāo)識(shí),用于區(qū)分不同的鎖持有者
* @param expireTime 鎖的過期時(shí)間,單位為毫秒
* @return 如果成功獲取鎖,返回 true;否則返回 false
*/
public boolean acquireLock(String lockKey, String requestId, long expireTime) {
// 使用 setIfAbsent 方法嘗試設(shè)置鍵值對(duì),如果鍵不存在則設(shè)置成功并返回 true,否則返回 false
Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, expireTime, TimeUnit.MILLISECONDS);
return result != null && result;
}
/**
* 釋放分布式鎖
*
* @param lockKey 鎖的鍵
* @param requestId 請(qǐng)求標(biāo)識(shí),用于確保只有鎖的持有者才能釋放鎖
* @return 如果成功釋放鎖,返回 true;否則返回 false
*/
public boolean releaseLock(String lockKey, String requestId) {
// 獲取當(dāng)前鎖的值
String currentValue = redisTemplate.opsForValue().get(lockKey);
// 檢查當(dāng)前鎖的值是否等于請(qǐng)求標(biāo)識(shí),確保只有鎖的持有者才能釋放鎖
if (currentValue != null && currentValue.equals(requestId)) {
// 刪除鎖鍵
return redisTemplate.delete(lockKey);
}
return false;
}
}
創(chuàng)建業(yè)務(wù)類用來測(cè)試
import com.wh.demo01.demos.web.utils.DistributedLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Date;
@Service
public class MyRedisService {
private final DistributedLock distributedLock;
// 通過構(gòu)造函數(shù)注入 DistributedLock
@Autowired
public MyRedisService(DistributedLock distributedLock) {
this.distributedLock = distributedLock;
}
/**
* 模擬需要同步執(zhí)行的方法
*/
public void someMethod() {
String lockKey = "myLockKey";
String requestId = "uniqueRequestId";
long expireTime = 10000; // 10 seconds
try {
// 嘗試獲取鎖
if (distributedLock.acquireLock(lockKey, requestId, expireTime)) {
// 獲取到鎖,執(zhí)行需要同步的操作
System.out.println(new Date() + "獲取鎖成功");
// 模擬業(yè)務(wù)操作
Thread.sleep(5000);
} else {
System.out.println(new Date() + "獲取鎖失敗");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
// 確保鎖在操作完成后被釋放
distributedLock.releaseLock(lockKey, requestId);
}
}
}
創(chuàng)建Controller
import com.wh.demo01.demos.web.service.MyRedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/redis")
public class RedisController {
@Autowired
private MyRedisService service;
@GetMapping("/test")
public void TestRedis(){
service.someMethod();
}
}
整個(gè)項(xiàng)目結(jié)構(gòu)如下:

使用idea的復(fù)制配置功能將該服務(wù)復(fù)制一份,并指定端口為8081,模擬分布式服務(wù):

使用接口調(diào)試工具分別向8080和8081端口發(fā)送請(qǐng)求:


結(jié)果如下:


可見在分布式鎖的影響下,someMethod方法在10秒內(nèi)只能被調(diào)用一次。
到此這篇關(guān)于Redis使用SETNX命令實(shí)現(xiàn)分布式鎖的文章就介紹到這了,更多相關(guān)Redis SETNX實(shí)現(xiàn)分布式鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Redis分布式鎖的幾種實(shí)現(xiàn)方法
- 使用Redis實(shí)現(xiàn)分布式鎖的代碼演示
- Redis分布式鎖使用及說明
- Redisson分布式鎖解鎖異常問題
- redis分布式鎖實(shí)現(xiàn)示例
- Redis 實(shí)現(xiàn)分布式鎖時(shí)需要考慮的問題解決方案
- Redis實(shí)現(xiàn)分布式鎖的示例代碼
- Redission實(shí)現(xiàn)分布式鎖lock()和tryLock()方法的區(qū)別小結(jié)
- 從原理到實(shí)踐分析?Redis?分布式鎖的多種實(shí)現(xiàn)方案
- Redis本地鎖和分布式鎖的區(qū)別小結(jié)
相關(guān)文章
Redisson分布式限流器RRateLimiter的使用及原理小結(jié)
本文主要介紹了Redisson分布式限流器RRateLimiter的使用及原理小結(jié),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-06-06
Redis數(shù)據(jù)結(jié)構(gòu)原理淺析
這篇文章主要為大家介紹了Redis數(shù)據(jù)結(jié)構(gòu)原理淺析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02
Redis數(shù)據(jù)過期策略的實(shí)現(xiàn)詳解
最近項(xiàng)目當(dāng)中遇到一個(gè)需求場(chǎng)景,需要清空一些存放在Redis的數(shù)據(jù),本文對(duì)Redis的過期機(jī)制簡(jiǎn)單的講解一下,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09
Redis中統(tǒng)計(jì)各種數(shù)據(jù)大小的方法
這篇文章主要介紹了Redis中統(tǒng)計(jì)各種數(shù)據(jù)大小的方法,本文使用PHP實(shí)現(xiàn)統(tǒng)計(jì)Redis內(nèi)存占用比較大的鍵,需要的朋友可以參考下2015-03-03
Redis遍歷海量數(shù)據(jù)的實(shí)現(xiàn)示例
本文主要介紹了 Redis遍歷海量數(shù)據(jù)的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-04-04

