通过Redis实现分布式锁
分布式鎖是控制分布式系統或不同系統之間共同訪問共享資源的一種鎖的實現;如果不同的系統或同一系統的不同主機間共享某一資源時,往往需要通過互斥來防止彼此干擾,以此來保證一致性。
分布式鎖需要解決的問題
互斥性
任一時刻只能有一個客戶端獲取鎖,不能有兩個客戶端都獲取到鎖
安全性
鎖只能被持有該鎖的客戶端刪除,不能由其他客戶端刪除
死鎖
獲取鎖的客戶端因為某些原因而宕機,而未能釋放鎖,其他客戶端再也不能獲取到該鎖而導致死鎖,此時需要有機制避免死鎖的發(fā)生
容錯
當部分節(jié)點(如Redis節(jié)點)宕機時,客戶端仍能獲取鎖和釋放鎖
如何通過Redis實現分布式鎖
第一種:使用SETNX與EXPIRE組合的方式
使用SETNX(SET IF NOT EXIST)指令:SETNX key value
“SETNX key value”:如果key不存在,則創(chuàng)建并賦值
時間復雜度:O(1),高效
返回值:設置成功(即“key不存在,創(chuàng)建并賦值成功”),返回1;
? 設置失敗(即“key存在,創(chuàng)建并賦值失敗”),返回0;
[外鏈圖片轉存失敗(img-a8aaf4GK-1567217454144)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1567154979106.png)]
正因為setnex有上述功能,并且操作是原子的,因此初期時被人們用來實現分布式鎖
我們在執(zhí)行某段代碼邏輯時,先嘗試使用setnex對某個key設值,如果設值成功,則證明此時沒有其他線程在執(zhí)行該段代碼或者占用該獨占資源,我們的線程就能順利地執(zhí)行該段代碼邏輯;如果設值失敗,則證明此時有其他程序或者線程占用該資源,那么當前線程就需要等待,直至下次的setnex成功。
但此時我們有個疑問:一旦某個線程成功setnex某個key,那這個key就會被該線程長久持有,后續(xù)其他線性該如何再次獲取到鎖?
因此,我們還需要解決下面一個問題:如何解決SETNEX長期有效的問題?,即為key設定一個過期時間。
使用EXPIRE指令:EXPIRE key seconds
“EXPIRE key seconds”:設置key的生存時間,當key過期時(生存時間為0),會被自動刪除
缺點:原子性得不到滿足
偽代碼
// 獲取Redis服務 RedisService redisService = SpringUtils.getBean(RedisService.class); // 執(zhí)行setnex并獲取返回狀態(tài) long status = redisService.setnx(key, "1");// 判斷是否設置成功 if (status == 1) {// 若設置成功,進而設置過期時間redisService.expire(key, expire);// 執(zhí)行獨占資源邏輯doOcuppiedWork(); }但是該段程序存在風險。
當程序執(zhí)行完第4行邏輯時,由于某些原因導致程序掛掉,導致未給key設置生存時間,則key會被一直占用著,這就意味著其他的線程永遠也無法執(zhí)行獨占資源邏輯。
這就是之前提到的,原子性得不到滿足。也就是說,雖然setnex是原子的,expire是原子的,但二者的組合操作卻不是原子的,即這種方法實現的分布式鎖使得原子性得不到滿足。
第二種:使用SET指令
從redis2.6.12版本開始,我們就可以使用SET操作將setnex和expire操作揉在一起執(zhí)行。
具體指令如下:
SET key value [EX seconds] [PX milliseconds] [NX|XX]
EX second:設置鍵的過期時間為second秒
PX millisecond:設置鍵的過期時間為millisecond毫秒
NX:只在鍵不存在時,才對鍵進行設置操作,與setnex相同
XX:只在鍵已經存在時,才對鍵進行設置操作,與setnex相反
SET操作成功完成時,返回OK;若設置了NX和XX,但因條件未達到而造成操作未執(zhí)行,則返回nil
這個“12345”為value,可以設置成request ID或線程ID,即可用來標識當前占用該資源的請求或者線程
即滿足分布式鎖的原子性需求。
在程序中可用以下的偽代碼邏輯實現分布式鎖
RedisService redisService = SpringUtils.getBean(RedisService.class); String result = redisService.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);if("OK".equals(result)) {// 執(zhí)行獨占資源邏輯doOcuppiedWork() }大量的key同時過期的注意事項
接下來,我們再了解下與key過期的相關性問題。
當大量的key集中過期,由于清除大量的key很耗時,會出現短暫的卡頓現象。
解決方案
在設置key的過期時間的時候,給每個key加上隨機值,將key的過期時間分散開來,避免卡頓現象的發(fā)生。
們再了解下與key過期的相關性問題。
當大量的key集中過期,由于清除大量的key很耗時,會出現短暫的卡頓現象。
解決方案
在設置key的過期時間的時候,給每個key加上隨機值,將key的過期時間分散開來,避免卡頓現象的發(fā)生。
總結
以上是生活随笔為你收集整理的通过Redis实现分布式锁的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 字节跳动算法题
- 下一篇: 剑指Offer(Java实现)按之字形顺