1. 前言
分布式應用中,有時我們需要一個方法在同一時間只能被一個線程執行。此時我們有可能會使用到分布式鎖。
分布式鎖需要具備以下特征:
- 互斥性 同一時刻鎖只能被一個線程持有。
- 超時釋放 超時釋放主要是用來避免死鎖,防止不必要的線程等待和資源浪費
- 可重入性 一個線程在持有鎖的情況下,可以再次請求加鎖
- 高性能,高可用 加鎖釋放鎖的操作盡量使用更少的資源,提高性能。同時也要保證高可用,防止分布式鎖意外失效
目前比較多的分布式鎖有下面的方案:
- 基于數據庫實現分布式鎖
- 基于緩存(redis, Hazelcast)等實現分布式鎖
- 基于Zookeeper實現分布式鎖
2. 數據庫分布式鎖
2.1基于表記錄的分布式鎖
在數據庫中創建一個鎖表,并且在需要的字段上創建唯一索引,使用鎖的時候就插入數據,插入成功則獲得鎖,執行結束后,就刪除數據。也可以加上version控制,使之成為樂觀鎖。
- 獲取鎖:成功插入數據
- 執行業務邏輯
- 釋放鎖:刪除數據
2.2基于數據庫行鎖的分布式鎖
使用select * For update來獲取數據庫數據鎖, where之后的條件加入唯一索引,則表示使用了行鎖。其分布式鎖使用順序如下。
- 獲取鎖:SELECT * FROM database_lock WHERE id = 1 FOR UPDATE;。
- 執行業務邏輯。
- 釋放鎖:COMMIT。
3 Zookeeper分布式鎖
Zookeepe可以實現分布式鎖主要是因為多個線程去Zookpeeper中創建同一個節點時,只有一個線程可以創建成功。
Zookeeper中有臨時節點,持久化節點。其中臨時節點在服務端session失效后,節點就會被刪除。相對而言,持久化節點在服務端session失效后,也不會被刪除,而是需要客戶端主動刪除。
在上述類型系節點之后增加一個數字后綴,即路徑+數字后綴,這樣可以保證其唯一性和有序性。
其分布式鎖實現原理如下:
- 創建一個lock目錄給分布式鎖使用
- 某個線程想要獲取鎖就在此目錄下創建臨時順序節點
- 獲取此目錄下的所有子節點,然后查找比自己序號小的節點,如果不存在,則當前線程創建的節點是最小節點,此時獲得鎖。
- 其他線程想要獲取鎖,同樣是創建臨時有序節點,如果是最小序號節點則獲得鎖,否則監聽比自己小的次小節點。
- 持有分布式鎖的線程操作完成之后,刪除自己的臨時節點,次大節點監聽到變更事件之后,判斷自己是最小序號節點的話,則獲得鎖。
Zookeeper實現分布式鎖具有高可用,可重入,阻塞等特點,由于臨時節點在客戶端斷開的時候就會被自動刪除,所以不用擔心死鎖問題。但是頻繁刪除和創建節點,性能上會比Redis分布式鎖低。
4 Redis分布式鎖
4.1 SETNX
setnx命令只會在key不存在的情況下將key設置為value值, 其中key和 value值均可以設置成和業務相關的的命名。但是不滿足超時釋放的要求。
如果使用expire設置過期時間,也有可能在setnx成功后,由于各種原因,expire沒有執行成功,從而導致鎖不能超時釋放。
4.2 SETNX 擴展命令
set key value [EX seconds] [PX milliseconds] [NX|XX]
#EX 設置過期時間,單位為秒 set lock VALUE EX 10
#PX 設置過期時間,單位為毫秒 set lock VALUE PX 10000
#NX key不存在時才設置key的值 set lock VALUE EX 10 NX
#XX key存在時才設置key SET lock VALUE EX 10 XX
set 擴展命令可以完全取代 SETNX, SETEX, PSETEX 等功能。
可以使用set擴展功能完成設置過期時間, 并且是原子操作。
上述分布式鎖也有一些問題:
- 如果線程獲取鎖之后,執行時間過長,鎖提前釋放。
- 如果線程A未執行完操作,鎖超時釋放,此時線程B又獲取了鎖。線程B持有鎖,但是A線程有可能執行DEL操作釋放鎖。
需要避免在長時間執行的任務中使用上述分布式鎖,而且未按時執行完的線程不影響其最終結果。另外可以在鎖的value設置一些唯一值,刪除key之前驗證是否持有鎖。并且驗證和刪除需要使用Lua腳本保證其刪除操作的原子性。
上述分布式鎖還需要解決一個可重入性的問題。
4.3 Redisson 分布式鎖
Redisson是基于Redis的Java內存數據網格,充分利用了Redis鍵值數據庫提供的一系列優勢。同時提供功能豐富的分布式鎖。
Resisson內部會有一個監控鎖的守護線程,在redisson實例被關閉前,不斷延長鎖的有效期。并且可以自定義超時檢查時間間隔,同時還可以指定加鎖時間。另外還支持公平鎖(Fair Lock),聯鎖(MultiLock),紅鎖(RedLock),讀寫鎖(ReadWriteLock)以及RSemaphore和RCountDownLatch等類似Java提供的各種多線程工具等。
其中RedLock是基于多個Redis集群關聯的鎖,可以大大提高鎖的可用性及安全性。
-
數據
+關注
關注
8文章
7232瀏覽量
90693 -
數據庫
+關注
關注
7文章
3879瀏覽量
65516 -
分布式
+關注
關注
1文章
969瀏覽量
75060 -
線程
+關注
關注
0文章
507瀏覽量
20009 -
服務端
+關注
關注
0文章
68瀏覽量
7159
發布評論請先 登錄
相關推薦
分布式軟件系統
分布式數據庫有什么優缺點?
HarmonyOS分布式數據庫,為啥這么牛?
【木棉花】分布式數據庫
分布式數據庫,什么是分布式數據庫
為什么我們需要分布式數據庫
數據庫如何走向分布式

評論