Redis(七)分布式锁
前面學習了Redis的數(shù)據(jù)結(jié)構(gòu)以及命令、Redis中的事務(wù)和Redis對Lua腳本的支持。
這一章就對Redis這些特性做一下實戰(zhàn)性應(yīng)用——基于Redis的分布式鎖實現(xiàn)。
Lock和Distributed Lock
在這之前先來認識下鎖(Lock)和分布式鎖(Distributed Lock):
In computer science, a lock or mutex (from mutual exclusion) is a synchronization mechanism for enforcing limits on access to a resource in an environment where there are many threads of execution. A lock is designed to enforce a mutual exclusion concurrency control policy.
參考wiki的解釋:在計算機科學領(lǐng)域中,鎖是為了限制多線程環(huán)境訪問一個資源的一種同步機制。鎖被設(shè)計相互排斥的并發(fā)策略。
Lock的前提條件:
- 同一臺機器上的共同資源;
- 多線程環(huán)境訪問共同資源;
Lock目標:
- 保證多線程訪問資源的一致性;
Lock的實現(xiàn):
- 單核處理器上禁用中斷,使得同步資源能夠被訪問結(jié)束;
- 硬件支持的原子指令,“比較和交換”等等,用于測試鎖是否是空閑,如果空閑獲取鎖;
Operating systems use lock managers to organise and serialise the access to resources. A distributed lock manager (DLM) runs in every machine in a cluster, with an identical copy of a cluster-wide lock database. In this way a DLM provides software applications which are distributed across a cluster on multiple machines with a means to synchronize their accesses to shared resources.
參考wiki解釋分布式鎖:操作系統(tǒng)用鎖管理器實現(xiàn)有組織有順序的訪問資源。分布式鎖運行在集群環(huán)境中的每臺機器上,使得數(shù)據(jù)具有相同的副本。分布式鎖提供分布式軟件應(yīng)用同步訪問共享資源。
Distribute Lock的前提條件:
- 分布式軟件應(yīng)用;
- 分布式軟件中的共享資源;
Distribute Lock目標:
- 保證分布式應(yīng)用訪問共享資源的一致性
Distribute Lock實現(xiàn)方式:
- 基于Redis實現(xiàn);
- 基于Zookeeper實現(xiàn);
- 基于Etcd或者Consul實現(xiàn);
- Google開發(fā)的Chubby(Lock Service);
獨占式鎖的特點和影響
按照用途、場景劃分,鎖的類型非常多。如:排它鎖(獨占式鎖)、共享鎖,自旋鎖、互斥鎖,讀鎖、寫鎖。但是在分布式環(huán)境中的所謂的分布式鎖,大多數(shù)情況下都是指:分布式獨占式鎖。
1.特點分析:
- 每次只能一個占用鎖;
- 可以重復(fù)進入鎖;
- 只有占用者才可以解鎖;
- 獲取鎖和釋放鎖都需要原子
- 不能產(chǎn)生死鎖
- 盡量滿足性能
本質(zhì):同步互斥,使得處理任務(wù)能夠一個一個逐步的過臨界資源。
造成的影響:
- 降低并發(fā)數(shù),使得多任務(wù)處理,只能一個一個的進行;
- 任務(wù)的換進換出造成切換上的開銷;
本質(zhì):使得吞吐量大打折扣。
基于Redis的實現(xiàn)
Redis實現(xiàn)分布式鎖的基礎(chǔ)
1.Redis本身就是單線程:
- 單個命令執(zhí)行具有原子性、無競態(tài)條件,這個特點符合一次只有一個客戶端爭用鎖;
2.Redis提供了set if not exists操作:
- 存在即不設(shè)置,這個特點符合鎖的獨占性(排它特點);
下面來先來看下獲取鎖:
return jedis.set(lockKey, lockValue, NX, EX, expireTime) != null ? true : false;這里使用set指令,具有原子操作特點,不會被其他客戶端操作中斷,在分布式環(huán)境中,是安全的,沒有競態(tài)條件產(chǎn)生,一次只能有一個客戶端爭用鎖;使用nx,即存在不設(shè)置,符合獨占特點;設(shè)置ex,有過期效果,不會產(chǎn)生永久獨占即死鎖;最后設(shè)置了lockValue,這樣就和當前加鎖任務(wù)做了綁定,后面可以用其作為解鎖的鑰匙;
再來看下解鎖操作:
static final String RELEASE_LOCK_LUA = "if redis.call('get', KEYS[1]) == ARGV[1] " +"then return redis.call('del', KEYS[1]) else return 0 end";Object result = jedis.eval(RELEASE_LOCK_LUA, 1, lockKey, lockValue);這里解鎖是用了Lua腳本,上篇文章中介紹了Redis內(nèi)置一個Lua解釋器,Redis調(diào)用解釋器執(zhí)行Lua腳本也是具有原子性的,即同一時刻只有一個客戶端的操作能被執(zhí)行,所以這里使用Lua腳本解鎖無競態(tài)條件;解鎖符合是占用鎖的任務(wù)釋放的原理;
但是以上實現(xiàn)的分布式鎖缺點是:
- 不具有重入性,即當前任務(wù)獲取了鎖,在下次獲取時將會死鎖;
- 不能自旋獲取,即獲取失敗時,將會立即返回失敗;
本人對其進行了改造,分別做了適應(yīng)以上兩種場景的分布式鎖,詳情可以戮[Distributed Lock],歡迎大家一起來完善。
總結(jié)
本文從What、Features、How的角度分析了分布式鎖。總的來說,單機應(yīng)用中的多線程或者多進程的鎖的放大版基本上就是分布式鎖了。萬變不離其宗,實現(xiàn)獨占鎖的關(guān)鍵性要素:
- 目標:互斥同步,資源訪問原子化;
- 實現(xiàn):一次只能有一個爭用到鎖,爭用過程是個原子過程,只能爭用到的解鎖,不會發(fā)生死鎖;
參考
Redis 分布式鎖的正確實現(xiàn)方式
Rewriting our lock
Lock
題外話
參考Rewriting our lock中使用setnx實現(xiàn)的分布式,嚴格意義上來說是有死鎖問題的。setnx和expire不具有原子性。當setnx成功后,expire前應(yīng)用發(fā)生宕機,這會導(dǎo)致鎖永遠不會過期,別的應(yīng)用始終爭用不到鎖。當然這種情況比較特殊,但是做代碼是一件嚴謹?shù)氖?#xff01;
轉(zhuǎn)載于:https://www.cnblogs.com/lxyit/p/9829132.html
總結(jié)
以上是生活随笔為你收集整理的Redis(七)分布式锁的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: js实现移动端图片预览:手势缩放, 手势
- 下一篇: JavaScript 调试