基于redis的分布式锁
概述
在之前, 我也使用redis做過(guò)分布式鎖, 當(dāng)時(shí)的做法是這樣的:
當(dāng)時(shí)覺(jué)得貌似沒(méi)什么問(wèn)題. 是我太天真了, 今天突然想到, 恩, 有問(wèn)題.
問(wèn)題
1.如果在第一步之后, 程序崩了, 沒(méi)有給鎖設(shè)置過(guò)期時(shí)間, 導(dǎo)致所有后續(xù)操作都無(wú)法正常獲取到鎖. 怎么破?
2.在A成功上鎖后, 因?yàn)镮O阻塞等原因, 執(zhí)行時(shí)間有點(diǎn)長(zhǎng), 鎖已經(jīng)過(guò)期了, 這時(shí)B過(guò)來(lái)成功上鎖, A在釋放鎖的時(shí)候釋放的就是B的鎖.
3.redis突然掛了. 如果redis突然掛了, 怎么辦? 當(dāng)然, 可以增加redis節(jié)點(diǎn), 主節(jié)點(diǎn)掛了, 從節(jié)點(diǎn)立刻補(bǔ)上. 但是, 主節(jié)點(diǎn)的數(shù)據(jù)同步到從節(jié)點(diǎn)也是需要時(shí)間的吧. 假設(shè)一個(gè)場(chǎng)景:
這個(gè)時(shí)候, 分布式鎖就失效了.
解決
那么有沒(méi)有辦法解決上面的問(wèn)題呢? 我到萬(wàn)能的谷歌上找了一下, 恩, 真的有.
上面的問(wèn)題一個(gè)一個(gè)解決.
問(wèn)題一
如何避免沒(méi)有給鎖設(shè)置過(guò)期時(shí)間的問(wèn)題?
其實(shí)看看就知道了, 問(wèn)題出在設(shè)置key和設(shè)置value分成兩條命令執(zhí)行, 所以導(dǎo)致如果在 setnx命令執(zhí)行過(guò)后, 程序崩潰, expire命令沒(méi)有正常執(zhí)行, 將其合并為一條命令就好啦.
set key value NX PX 5000
其中NX表示存在則不設(shè)置, PX表示過(guò)期時(shí)間.
如此, 至少可以保證不會(huì)出現(xiàn)沒(méi)有過(guò)期時(shí)間的鎖了
問(wèn)題二
如何避免A釋放了B的鎖.
如何避免釋放了其他人的鎖呢? 換個(gè)問(wèn)題, 如何保證這個(gè)鎖是你加的呢? so easy, 加鎖的時(shí)候, 講value值設(shè)置成一個(gè)只有我知道的隨機(jī)數(shù)字, 釋放的時(shí)候看看值是不是我的就行了.
如此在釋放的時(shí)候需要兩步操作:
當(dāng)然, 為了保證釋放鎖操作的原子性, 這兩步操作最好也能合并為一步操作. 那redis如何實(shí)現(xiàn)值是否相同的判斷呢? Lua腳本.
簡(jiǎn)單介紹一下
eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 argv1 argv2 # 看懂了吧, 哈哈 # eval 是redis內(nèi)置的命令 # 第一個(gè)參數(shù)是運(yùn)行的腳本邏輯 # 第二個(gè)參數(shù)表示后面有幾個(gè)key # 第五個(gè)參數(shù)開(kāi)始就是附加參數(shù), 在腳本邏輯中使用的所以, 腳本內(nèi)容如下:
if redis.call("get",KEYS[1]) == ARGV[1] thenreturn redis.call("del",KEYS[1]) elsereturn 0 end如此, 至少可以保證不會(huì)出現(xiàn)A釋放了B鎖的情況了
問(wèn)題三
如何保證在主節(jié)點(diǎn)掛掉的時(shí)候, 從節(jié)點(diǎn)接替后, 不會(huì)重復(fù)獲得鎖?
官網(wǎng)上提供了一個(gè)方法, 從多個(gè)redis實(shí)例同時(shí)獲取鎖. 因?yàn)槲覜](méi)看太明白, 之后看懂了在說(shuō)吧. 過(guò)…
其實(shí), 如果不是處理金錢(qián)這種不容出錯(cuò)的業(yè)務(wù), 這種小概率事件個(gè)人覺(jué)得還是可以容忍的.
總結(jié)
最終, 在redis單機(jī)下實(shí)現(xiàn)的分布式鎖操作如下:
# 獲取分布式鎖,過(guò)期時(shí)間可調(diào) set lock_key random_value NX PX 5000 # ...do something # 釋放分布式鎖 eval "if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end" 1 lock_key random_value總結(jié)
以上是生活随笔為你收集整理的基于redis的分布式锁的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 图片饱和度_摄影后期完全调色指南(三):
- 下一篇: 计算机网络-信道复用技术