Redis分布式鎖的幾種實(shí)現(xiàn)方法
更新時(shí)間:2025年04月16日 08:25:21 作者:今天多喝熱水
本文主要介紹了Redis分布式鎖的幾種實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
Redis基本命令:
// 設(shè)置鍵myKey的值為myValue,并且該鍵在10秒后過(guò)期 SET myKey myValue EX 10 // 設(shè)置鍵myKey的值為myValue,并且該鍵在1000毫秒(1秒)后過(guò)期 SET myKey myValue PX 1000 // 指定key過(guò)期時(shí)間,單位是秒,過(guò)期后自動(dòng)刪除 EXPIRE key_name second_num // 指定key過(guò)期時(shí)間,單位是毫秒,過(guò)期后自動(dòng)刪除 PEXPIRE key_name millisecond_num // 返回key過(guò)期時(shí)間 TTL key_name SET key value //設(shè)置鍵key的值為value SETNX key value //只有在鍵key不存在的情況下,將key的值設(shè)置為value SETEX key seconds value //將鍵key的值設(shè)置為value,并且超時(shí)時(shí)間為seconds秒 PSETEX key milliseconds value //將鍵key的值設(shè)置為value,并且超時(shí)時(shí)間為milliseconds毫秒
一、基礎(chǔ)方案:SETNX命令實(shí)現(xiàn)
public class SimpleRedisLock {
private Jedis jedis;
private String lockKey;
public SimpleRedisLock(Jedis jedis, String lockKey) {
this.jedis = jedis;
this.lockKey = lockKey;
}
public boolean tryLock() {
Long result = jedis.setnx(lockKey, "locked");
if (result == 1) {
jedis.expire(lockKey, 30); // 設(shè)置過(guò)期時(shí)間
return true;
}
return false;
}
public void unlock() {
jedis.del(lockKey);
}
}
// 使用示例
Jedis jedis = new Jedis("localhost");
SimpleRedisLock lock = new SimpleRedisLock(jedis, "order_lock");
try{
if(lock.tryLock()){
// 業(yè)務(wù)邏輯
}
} finally {
lock.unlock();
}
問(wèn)題分析:
- 非原子操作:setnx和expire非原子操作,可能產(chǎn)生死鎖
- 鎖誤刪:任何客戶端都可以刪除鎖
- 不可重入:同一線程重復(fù)獲取會(huì)失敗
二、改進(jìn)方案:原子SET命令
public class AtomicRedisLock {
private Jedis jedis;
private String lockKey;
private String clientId;
public SimpleRedisLock(Jedis jedis, String lockKey) {
this.jedis = jedis;
this.lockKey = lockKey;
this.clientId = UUID.randomUUID().toString();
}
public boolean tryLock(int expireSeconds) {
String result = jedis.set(lockKey, clientId, SetParams.setParams().nx().ex(expireSeconds));
return "OK".equals(result);
}
public boolean unlock() {
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, Collections.singletonList(lockKey), Collections.singletonList(clientId));
return result.equals(1L);
}
}
// 使用示例
Jedis jedis = new Jedis("localhost");
AtomicRedisLock lock = new AtomicRedisLock(jedis, "payment_lock");
try{
if(lock.tryLock(30)){
// 業(yè)務(wù)邏輯
}
} finally {
lock.unlock();
}核心改進(jìn):
- 使用原子SET命令:SET key value NX EX
- Lua腳本保證刪除原子性
- 客戶端唯一標(biāo)識(shí)防止誤刪
仍然存在的問(wèn)題:
- 鎖續(xù)期困難
- 單點(diǎn)故障風(fēng)險(xiǎn)
- 業(yè)務(wù)超時(shí)可能導(dǎo)致鎖失效
三、高可用方案:RedLock算法
public class RedLock {
pprivate List<Jedis> jedisList;
private String lockKey;
private String clientId;
private int quorum;
public RedLock(List<Jedis> jedisList, String lockKey) {
this.jedisList = jedisList;
this.lockKey = lockKey;
this.clientId = UUID.randomUUID().toString();
this.quorum = jedisList.size() / 2 + 1;
}
public boolean tryLock(int expireMillis) {
long startTime = System.currentTimeMillis();
// 第一階段:嘗試獲取多數(shù)節(jié)點(diǎn)鎖
int successCount = 0;
for (Jedis jedis : jedisList) {
if (tryAcquire(jedis, expireMillis)) {
successCount++;
}
if ((System.currentTimeMillis() - startTime) > expireMillis) {
break;
}
}
// 第二階段:驗(yàn)證鎖有效性
if (successCount >= quorum) {
long validityTime = expireMillis - (System.currentTimeMillis() - startTime);
return validityTime > 0;
}
// 第三階段:釋放已獲得的鎖
for (Jedis jedis : jedisList) {
release(jedis);
}
return false;
}
private boolean tryAcquire(Jedis jedis, long expireMillis) {
try {
String result = jedis.set(lockKey, clientId, SetParams.setParams().nx().px(expireMillis));
return "OK".equals(result);
} catch (Exception e) {
return false;
}
}
private void release(Jedis jedis) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) else return 0 end";
jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(clientId));
}
}部署要求:
- 至少5個(gè)獨(dú)立Redis實(shí)例
- 節(jié)點(diǎn)間時(shí)鐘同步
- 需要配置合理的超時(shí)時(shí)間
適用場(chǎng)景:
- 金融交易等對(duì)可靠性要求極高的場(chǎng)景
- 需要跨機(jī)房部署的分布式系統(tǒng)
四、生產(chǎn)級(jí)方案:Redisson實(shí)現(xiàn)
// 配置Redisson客戶端
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redisson = Redisson.create(config);
// 獲取鎖對(duì)象
RLock lock = redisson.getLock("orderLock");
try {
// 嘗試加鎖,最多等待100秒,鎖定后30秒自動(dòng)解鎖
boolean isLock = lock.tryLock(100, 30, TimeUnit.SECONDS);
if (isLock) {
// 處理業(yè)務(wù)
}
} finally {
lock.unlock();
}
// 關(guān)閉客戶端
redisson.shutdown();
// 自動(dòng)續(xù)期機(jī)制(Watchdog),Watchdog實(shí)現(xiàn)原理(簡(jiǎn)化版)
private void renewExpiration() {
Timeout task = commandExecutor.schedule(() -> {
if (redisClient.eval(...)){ // 檢查是否仍持有鎖
expireAsync(); // 續(xù)期
renewExpiration(); // 遞歸調(diào)用
}
}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
}
核心特性:
- 支持可重入鎖
- 提供公平鎖、聯(lián)鎖(MultiLock)、紅鎖(RedLock)實(shí)現(xiàn)
- 完善的故障處理機(jī)制
到此這篇關(guān)于Redis分布式鎖的幾種實(shí)現(xiàn)方法的文章就介紹到這了,更多相關(guān)Redis分布式鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
您可能感興趣的文章:
- 使用Redis實(shí)現(xiàn)分布式鎖的代碼演示
- Redis使用SETNX命令實(shí)現(xiàn)分布式鎖
- Redis分布式鎖使用及說(shuō)明
- Redisson分布式鎖解鎖異常問(wèn)題
- redis分布式鎖實(shí)現(xiàn)示例
- Redis 實(shí)現(xiàn)分布式鎖時(shí)需要考慮的問(wèn)題解決方案
- Redis實(shí)現(xiàn)分布式鎖的示例代碼
- Redission實(shí)現(xiàn)分布式鎖lock()和tryLock()方法的區(qū)別小結(jié)
- 從原理到實(shí)踐分析?Redis?分布式鎖的多種實(shí)現(xiàn)方案
- Redis本地鎖和分布式鎖的區(qū)別小結(jié)
相關(guān)文章
Redis權(quán)限和訪問(wèn)控制的實(shí)現(xiàn)示例
Redis提供了一些機(jī)制來(lái)保護(hù)敏感數(shù)據(jù)和限制對(duì)Redis服務(wù)器的訪問(wèn),本文主要介紹了Redis權(quán)限和訪問(wèn)控制的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下2023-12-12
利用redis實(shí)現(xiàn)分布式鎖,快速解決高并發(fā)時(shí)的線程安全問(wèn)題
這篇文章主要介紹了利用redis實(shí)現(xiàn)分布式鎖,快速解決高并發(fā)時(shí)的線程安全問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-01-01
Redis?使用?List?實(shí)現(xiàn)消息隊(duì)列的優(yōu)缺點(diǎn)
這篇文章主要介紹了Redis?使用?List?實(shí)現(xiàn)消息隊(duì)列有哪些利弊,小編結(jié)合消息隊(duì)列的特點(diǎn)一步步帶大家分析使用?Redis?的?List?作為消息隊(duì)列的實(shí)現(xiàn)原理,并分享如何把?SpringBoot?與?Redission?整合運(yùn)用到項(xiàng)目中,需要的朋友可以參考下2022-01-01

