這篇文章主要介紹“redis分布式鎖的實現(xiàn)以及用法”,在日常操作中,相信很多人在redis分布式鎖的實現(xiàn)以及用法問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”redis分布式鎖的實現(xiàn)以及用法”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習吧!
創(chuàng)新互聯(lián)主要從事網(wǎng)站制作、網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)招遠,十載網(wǎng)站建設(shè)經(jīng)驗,價格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):18982081108
redis加鎖命令:
SETNX resource_name my_random_value PX 30000
這個命令的作用是在只有這個key不存在的時候才會設(shè)置這個key的值(NX選項的作用),超時時間設(shè)為30000毫秒(PX選項的作用) 這個key的值設(shè)為“my_random_value”。這個值必須在所有獲取鎖請求的客戶端里保持唯一。
SETNX 值保持唯一的是為了確保安全的釋放鎖,避免誤刪其他客戶端得到的鎖。舉個例子,一個客戶端拿到了鎖,被某個操作阻塞了很長時間,過了超時時間后自動釋放了這個鎖,然后這個客戶端之后又嘗試刪除這個其實已經(jīng)被其他客戶端拿到的鎖。所以單純的用DEL指令有可能造成一個客戶端刪除了其他客戶端的鎖,通過校驗這個值保證每個客戶端都用一個隨機字符串’簽名’了,這樣每個鎖就只能被獲得鎖的客戶端刪除了。
既然釋放鎖時既需要校驗這個值又需要刪除鎖,那么就需要保證原子性,redis支持原子地執(zhí)行一個lua腳本,所以我們通過lua腳本實現(xiàn)原子操作。代碼如下:
if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end
如果在加鎖和釋放鎖之間的邏輯執(zhí)行得太長,以至于超出了鎖的超時限制,就會出現(xiàn)問題。因為這時候第一個線程持有的鎖過期了,臨界區(qū)的邏輯還沒有執(zhí)行完,這個時候第二個線程就提前重新持有了這把鎖,導(dǎo)致臨界區(qū)代碼不能得到嚴格的串行執(zhí)行。
不難發(fā)現(xiàn)正常情況下鎖操作完后都會被手動釋放,常見的解決方案是調(diào)大鎖的超時時間,之后若再出現(xiàn)超時帶來的并發(fā)問題,人工介入修正數(shù)據(jù)。這也不是一個完美的方案,因為但業(yè)務(wù)邏輯執(zhí)行時間是不可控的,所以還是可能出現(xiàn)超時,當前線程的邏輯沒有執(zhí)行完,其它線程乘虛而入。并且如果鎖超時時間設(shè)置過長,當持有鎖的客戶端宕機,釋放鎖就得依靠redis的超時時間,這將導(dǎo)致業(yè)務(wù)在一個超時時間周期內(nèi)不可用。
基本上,如果在執(zhí)行計算期間發(fā)現(xiàn)鎖快要超時了,客戶端可以給redis服務(wù)實例發(fā)送一個Lua腳本讓redis服務(wù)端延長鎖的時間,只要這個鎖的key還存在而且值還等于客戶端設(shè)置的那個值。 客戶端應(yīng)當只有在失效時間內(nèi)無法延長鎖時再去重新獲取鎖(基本上這個和獲取鎖的算法是差不多的)。
當鎖超時時間快到期且邏輯未執(zhí)行完,延長鎖超時時間的偽代碼:
if redis.call("get",KEYS[1]) == ARGV[1] then redis.call("set",KEYS[1],ex=3000) else getDLock();//重新獲取鎖
生產(chǎn)中redis一般是主從模式,主節(jié)點掛掉時,從節(jié)點會取而代之,客戶端上卻并沒有明顯感知。原先第一個客戶端在主節(jié)點中申請成功了一把鎖,但是這把鎖還沒有來得及同步到從節(jié)點,主節(jié)點突然掛掉了。然后從節(jié)點變成了主節(jié)點,這個新的節(jié)點內(nèi)部沒有這個鎖,所以當另一個客戶端過來請求加鎖時,立即就批準了。這樣就會導(dǎo)致系統(tǒng)中同樣一把鎖被兩個客戶端同時持有,不安全性由此產(chǎn)生。
不過這種不安全也僅僅是在主從發(fā)生 failover 的情況下才會產(chǎn)生,而且持續(xù)時間極短,業(yè)務(wù)系統(tǒng)多數(shù)情況下可以容忍。
如果你很在乎高可用性,希望掛了一臺 redis 完全不受影響,可以考慮 redlock。 Redlock 算法是由Antirez 發(fā)明的,它的流程比較復(fù)雜,不過已經(jīng)有了很多開源的 library 做了良好的封裝,用戶可以拿來即用,比如 redlock-py。
import redlock addrs = [{ "host": "localhost", "port": 6379, "db": 0 }, { "host": "localhost", "port": 6479, "db": 0 }, { "host": "localhost", "port": 6579, "db": 0 }] dlm = redlock.Redlock(addrs) success = dlm.lock("user-lck-laoqian", 5000) if success: print 'lock success' dlm.unlock('user-lck-laoqian') else: print 'lock failed'
RedLock算法的核心原理:
使用N個完全獨立、沒有主從關(guān)系的Redis master節(jié)點以保證他們大多數(shù)情況下都不會同時宕機,N一般為奇數(shù)。一個客戶端需要做如下操作來獲取鎖:
1.獲取當前時間(單位是毫秒)。
2.輪流用相同的key和隨機值在N個節(jié)點上請求鎖,在這一步里,客戶端在每個master上請求鎖時,會有一個和總的鎖釋放時間相比小的多的超時時間。比如如果鎖自動釋放時間是10秒鐘,那每個節(jié)點鎖請求的超時時間可能是5-50毫秒的范圍,這個可以防止一個客戶端在某個宕掉的master節(jié)點上阻塞過長時間,如果一個master節(jié)點不可用了,我們應(yīng)該盡快嘗試下一個master節(jié)點。
3.客戶端計算第二步中獲取鎖所花的時間,只有當客戶端在大多數(shù)master節(jié)點上成功獲取了鎖((N/2) +1),而且總共消耗的時間不超過鎖釋放時間,這個鎖就認為是獲取成功了。
4.如果鎖獲取成功了,那現(xiàn)在鎖自動釋放時間就是最初的鎖釋放時間減去之前獲取鎖所消耗的時間。
5.如果鎖獲取失敗了,不管是因為獲取成功的鎖不超過一半(N/2+1)還是因為總消耗時間超過了鎖釋放時間,客戶端都會到每個master節(jié)點上釋放鎖,即便是那些他認為沒有獲取成功的鎖。
Redis 提供了非常豐富的指令集,但是用戶依然不滿足,希望可以自定義擴充若干指令來完成一些特定領(lǐng)域的問題。Redis 為這樣的用戶場景提供了 lua 腳本支持,用戶可以向服務(wù)器發(fā)送 lua 腳本來執(zhí)行自定義動作,獲取腳本的響應(yīng)數(shù)據(jù)。Redis 服務(wù)器會單線程原子性執(zhí)行 lua 腳本,保證 lua 腳本在處理的過程中不會被任意其它請求打斷。
redis-lua交互--.png
要實現(xiàn)可重入鎖,方法很簡單,當加鎖失敗時判斷鎖的值是不是跟當前線程設(shè)置值相同,偽代碼如下:
if setnx == 0 if get(key) == my_random_value //重入 else //不可重入 else //獲取了鎖,等價于可重入
到此,關(guān)于“redis分布式鎖的實現(xiàn)以及用法”的學(xué)習就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習,快去試試吧!若想繼續(xù)學(xué)習更多相關(guān)知識,請繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>
文章標題:redis分布式鎖的實現(xiàn)以及用法
鏈接地址:http://weahome.cn/article/gsegsj.html