Redis分布式鎖的幾種實現(xiàn)方法
更新時間:2025年04月16日 08:25:21 作者:今天多喝熱水
本文主要介紹了Redis分布式鎖的幾種實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
Redis基本命令:
// 設置鍵myKey的值為myValue,并且該鍵在10秒后過期 SET myKey myValue EX 10 // 設置鍵myKey的值為myValue,并且該鍵在1000毫秒(1秒)后過期 SET myKey myValue PX 1000 // 指定key過期時間,單位是秒,過期后自動刪除 EXPIRE key_name second_num // 指定key過期時間,單位是毫秒,過期后自動刪除 PEXPIRE key_name millisecond_num // 返回key過期時間 TTL key_name SET key value //設置鍵key的值為value SETNX key value //只有在鍵key不存在的情況下,將key的值設置為value SETEX key seconds value //將鍵key的值設置為value,并且超時時間為seconds秒 PSETEX key milliseconds value //將鍵key的值設置為value,并且超時時間為milliseconds毫秒
一、基礎方案:SETNX命令實現(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); // 設置過期時間 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è)務邏輯 } } finally { lock.unlock(); }
問題分析:
- 非原子操作:setnx和expire非原子操作,可能產生死鎖
- 鎖誤刪:任何客戶端都可以刪除鎖
- 不可重入:同一線程重復獲取會失敗
二、改進方案:原子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è)務邏輯 } } finally { lock.unlock(); }
核心改進:
- 使用原子SET命令:SET key value NX EX
- Lua腳本保證刪除原子性
- 客戶端唯一標識防止誤刪
仍然存在的問題:
- 鎖續(xù)期困難
- 單點故障風險
- 業(yè)務超時可能導致鎖失效
三、高可用方案: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(); // 第一階段:嘗試獲取多數節(jié)點鎖 int successCount = 0; for (Jedis jedis : jedisList) { if (tryAcquire(jedis, expireMillis)) { successCount++; } if ((System.currentTimeMillis() - startTime) > expireMillis) { break; } } // 第二階段:驗證鎖有效性 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個獨立Redis實例
- 節(jié)點間時鐘同步
- 需要配置合理的超時時間
適用場景:
- 金融交易等對可靠性要求極高的場景
- 需要跨機房部署的分布式系統(tǒng)
四、生產級方案:Redisson實現(xiàn)
// 配置Redisson客戶端 Config config = new Config(); config.useSingleServer().setAddress("redis://127.0.0.1:6379"); RedissonClient redisson = Redisson.create(config); // 獲取鎖對象 RLock lock = redisson.getLock("orderLock"); try { // 嘗試加鎖,最多等待100秒,鎖定后30秒自動解鎖 boolean isLock = lock.tryLock(100, 30, TimeUnit.SECONDS); if (isLock) { // 處理業(yè)務 } } finally { lock.unlock(); } // 關閉客戶端 redisson.shutdown();
// 自動續(xù)期機制(Watchdog),Watchdog實現(xiàn)原理(簡化版) private void renewExpiration() { Timeout task = commandExecutor.schedule(() -> { if (redisClient.eval(...)){ // 檢查是否仍持有鎖 expireAsync(); // 續(xù)期 renewExpiration(); // 遞歸調用 } }, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS); }
核心特性:
- 支持可重入鎖
- 提供公平鎖、聯(lián)鎖(MultiLock)、紅鎖(RedLock)實現(xiàn)
- 完善的故障處理機制
到此這篇關于Redis分布式鎖的幾種實現(xiàn)方法的文章就介紹到這了,更多相關Redis分布式鎖內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
redis cluster集群模式下實現(xiàn)批量可重入鎖
本文主要介紹了使用redis cluster集群版所遇到的問題解決方案及redis可重入鎖是否會有死鎖的問題等,具有一定的參考價值,感興趣的可以了解一下2024-02-02解讀redis?slaveof命令執(zhí)行后為什么需要清庫重新同步
這篇文章主要介紹了redis?slaveof命令執(zhí)行后為什么需要清庫重新同步,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-04-04