分布式系統中,多個節點都需要訪問乙個臨界資源,但是同一時刻只能有乙個節點可以訪問,為了解決這個問題就是要通過分布式互斥來實現;分布式鎖就是實現分布式互斥的一種實現方式。
鎖是實現多執行緒同時訪問同一共享資源,保證同一時刻只有乙個執行緒可訪問共享資源所做的一種標記。
分布式鎖是指分布式環境下,系統部署在多個機器中,實現多程序分布式互斥的一種鎖。為了保證多個程序能看到鎖,鎖被存在公共儲存(比如 redis、資料庫等三方儲存中),以實現多個程序併發訪問同乙個臨界資源,同一時刻只有乙個程序可訪問共享資源,確保資料的一致性。
分布式鎖的三種實現方法
1.基於關聯式資料庫實現分布式鎖
(1)建立一張鎖表,然後通過操作該表中的資料來實現。
(2)當我們要鎖住某個資源時,就在該表中增加一條記錄,想要釋放鎖的時候就刪除這條記錄。
(3)資料庫對共享資源做了唯一性約束,如果有多個請求被同時提交到資料庫的話,資料庫會保證只有乙個操作可以成功,操作成功的那個執行緒就獲得了訪問共享資源的鎖。
缺點:
1.因為資料庫需要落到硬碟上,頻繁讀取資料庫會導致 io 開銷大,因此這種分布式鎖適用於併發量低,對效能要求低的場景。
2.容易引起單點故障。
2.基於快取實現分布式鎖
把資料存放在計算機記憶體中,不需要寫入磁碟,減少了 io 讀寫。
例項:redis 通過佇列來維持程序訪問共享資源的先後順序。
(1)redis 通常可以使用 setnx(key, value) 函式來實現分布式鎖。
(2)key 和 value 就是基於快取的分布式鎖的兩個屬性,其中 key 表示鎖 id,value = currenttime + timeout,表示當前時間 + 超時時間。
(3)某個程序獲得 key 這把鎖後,如果在 value 的時間內未釋放鎖,系統就會主動釋放鎖。
(4)setnx 函式的返回值有 0 和 1:返回 1,說明該伺服器獲得鎖,setnx 將 key 對應的 value 設定為當前時間 + 鎖的有效時間。返回 0,說明其他伺服器已經獲得了鎖,程序不能進入臨界區。該伺服器可以不斷嘗試 setnx 操作,以獲得鎖。
3.基於 zookeeper 實現分布式鎖
zookeeper 基於樹形資料儲存結構實現分布式鎖。
節點型別:
持久節點。這是預設的節點型別,一直存在於 zookeeper 中。
持久順序節點。也就是說,在建立節點時,zookeeper 根據節點建立的時間順序對節點進行編號。
臨時節點。與持久節點不同,當客戶端與 zookeeper 斷開連線後,該程序建立的臨時節點就會被刪除。
臨時順序節點。按時間順序編號的臨時節點。
以電商售賣吹風機的場景為例。假設使用者 a、b、c 同時在 11 月 11 日的零點整提交了購買吹風機的請求,zookeeper 會採用如下方法來實現分布式鎖:
1.在與該方法對應的持久節點 shared_lock 的目錄下,為每個程序建立乙個臨時順序節點。吹風機就是乙個擁有 shared_lock 的目錄,當有人買吹風機時,會為他建立乙個臨時順序節點。
2.每個程序獲取 shared_lock 目錄下的所有臨時節點列表,註冊子節點變更的 watcher,並監聽節點。
3.每個節點確定自己的編號是否是 shared_lock 下所有子節點中最小的,若最小,則獲得鎖。例如,使用者 a 的訂單最先到伺服器,因此建立了編號為 1 的臨時順序節點 locknode1。該節點的編號是持久節點目錄下最小的,因此獲取到分布式鎖,可以訪問臨界資源,從而可以購買吹風機。
4.若本程序對應的臨時節點編號不是最小的,則分為兩種情況:
a. 本程序為讀請求,如果比自己序號小的節點中有寫請求,則等待;
b. 本程序為寫請求,如果比自己序號小的節點中有讀請求,則等待。
例如,使用者 b 也想要買吹風機,但在他之前,使用者 c 想看看吹風機的庫存量。因此,使用者 b 只能等使用者 a 買完吹風機、使用者 c 查詢完庫存量後,才能購買吹風機。
zookeeper 分布式鎖的可靠性最高,有封裝好的框架,很容易實現分布式鎖的功能,並且幾乎解決了資料庫鎖和快取式鎖的不足。
1.利用租約在etcd集群中建立乙個key,這個key有兩種形態,存在和不存在,而這兩種形態就是互斥量。
2.如果這個key不存在,那麼執行緒建立key,成功則獲取到鎖,該key就為存在狀態。
3.如果該key已經存在,那麼執行緒就不能建立key,則獲取鎖失敗。
在使用該鎖時,需要傳入ttl,conf,key欄位來初始化鎖
type etcdmutex struct
func(em *etcdmutex)init()error
em.txn = clientv3.newkv(client).txn(context.todo())
em.lease = clientv3.newlease(client)
leaseresp,err := em.lease.grant(context.todo(),em.ttl)
if err != nil
ctx,em.cancel = context.withcancel(context.todo())
em.leaseid = leaseresp.id
_,err = em.lease.keepalive(ctx,em.leaseid)
return err
}
func(em *etcdmutex)lock()error
//lock:
em.txn.if(clientv3.compare(clientv3.createrevision(em.key),"=",0)).
then(clientv3.opput(em.key,"",clientv3.withlease(em.leaseid))).
else()
txnresp,err := em.txn.commit()
if err != nil
if !txnresp.succeeded
return nil
}
func(em *etcdmutex)unlock()
func main(),
dialtimeout: 5 * time.second,
}emutex1 := &etcdmutex
emutex2 := &etcdmutex
//groutine1
go func()
fmt.println("groutine1搶鎖成功")
time.sleep(10*time.second)
defer emutex.unlock()
}()//groutine2
go func()
fmt.println("groutine2搶鎖成功")
defer emutex.unlock()
}()time.sleep(30*time.second)
}
分布式 分布式鎖
本質是利用redis的setnx 方法的特性來加鎖,setnx 即key不存在則設定key,否則直接返回false,要求在分布式系統中使用同乙個redis服務,以下提供兩種解決方案 1 直接使用redistemplate 這其實並不能完全保證高併發下的安全問題,因為可能在鎖過期之後該執行緒尚未執行完...
分布式專題 分布式鎖
在傳統的單體應用架構中,遇到併發安全性問題時我們可以通過同步鎖synchronized,同步 塊,reentrantlock等方式都可以解決,但隨著業務的發展,單體應用架構不能滿足龐大的使用者請求量,於是分布式系統應用而生,在分布式系統中,由於每個系統都執行在不同的伺服器上,有著不同的jvm,所以j...
分布式 分布式事務
是資料庫執行過程中的乙個邏輯單位,由乙個有限的資料庫操作序列構成。事務的acid四大特性 原子性 atomicity 事務作為乙個整體被執行。一致性 consistency 從乙個一致的狀態轉換到另乙個一致的狀態。隔離性 isolation 多個事務併發執行時,併發事務之間互相影響的程度。永續性 d...