前面學習了Redis的數據結構以及命令、Redis中的事務和Redis對Lua腳本的支持。redis
這一章就對Redis這些特性作一下實戰性應用——基於Redis的分佈式鎖實現。安全
在這以前先來認識下鎖(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的解釋:在計算機科學領域中,鎖是爲了限制多線程環境訪問一個資源的一種同步機制。鎖被設計相互排斥的併發策略。併發
Lock的前提條件:app
Lock目標:分佈式
Lock的實現:ide
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解釋分佈式鎖:操做系統用鎖管理器實現有組織有順序的訪問資源。分佈式鎖運行在集羣環境中的每臺機器上,使得數據具備相同的副本。分佈式鎖提供分佈式軟件應用同步訪問共享資源。學習
Distribute Lock的前提條件:
Distribute Lock目標:
Distribute Lock實現方式:
按照用途、場景劃分,鎖的類型很是多。如:排它鎖(獨佔式鎖)、共享鎖,自旋鎖、互斥鎖,讀鎖、寫鎖。可是在分佈式環境中的所謂的分佈式鎖,大多數狀況下都是指:分佈式獨佔式鎖。
1.特色分析:
本質:同步互斥,使得處理任務可以一個一個逐步的過臨界資源。
形成的影響:
本質:使得吞吐量大打折扣。
1.Redis自己就是單線程:
2.Redis提供了set if not exists操做:
下面來先來看下獲取鎖:
return jedis.set(lockKey, lockValue, NX, EX, expireTime) != null ? true : false;
這裏使用set指令,具備原子操做特色,不會被其餘客戶端操做中斷,在分佈式環境中,是安全的,沒有競態條件產生,一次只能有一個客戶端爭用鎖;使用nx,即存在不設置,符合獨佔特色;設置ex,有過時效果,不會產生永久獨佔即死鎖;最後設置了lockValue,這樣就和當前加鎖任務作了綁定,後面能夠用其做爲解鎖的鑰匙;
再來看下解鎖操做:
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內置一個Lua解釋器,Redis調用解釋器執行Lua腳本也是具備原子性的,即同一時刻只有一個客戶端的操做能被執行,因此這裏使用Lua腳本解鎖無競態條件;解鎖符合是佔用鎖的任務釋放的原理;
可是以上實現的分佈式鎖缺點是:
本人對其進行了改造,分別作了適應以上兩種場景的分佈式鎖,詳情能夠戮[Distributed Lock],歡迎你們一塊兒來完善。
本文從What、Features、How的角度分析了分佈式鎖。總的來講,單機應用中的多線程或者多進程的鎖的放大版基本上就是分佈式鎖了。萬變不離其宗,實現獨佔鎖的關鍵性要素:
Redis 分佈式鎖的正確實現方式
Rewriting our lock
Lock
參考Rewriting our lock中使用setnx實現的分佈式,嚴格意義上來講是有死鎖問題的。setnx和expire不具備原子性。當setnx成功後,expire前應用發生宕機,這會致使鎖永遠不會過時,別的應用始終爭用不到鎖。固然這種狀況比較特殊,可是作代碼是一件嚴謹的事!