分布式概念 分布式鎖(分布式互斥的一種實現方式)

2021-10-06 13:15:55 字數 3746 閱讀 7362

分布式系統中,多個節點都需要訪問乙個臨界資源,但是同一時刻只能有乙個節點可以訪問,為了解決這個問題就是要通過分布式互斥來實現;分布式鎖就是實現分布式互斥的一種實現方式。

鎖是實現多執行緒同時訪問同一共享資源,保證同一時刻只有乙個執行緒可訪問共享資源所做的一種標記。

分布式鎖是指分布式環境下,系統部署在多個機器中,實現多程序分布式互斥的一種鎖。為了保證多個程序能看到鎖,鎖被存在公共儲存(比如 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...