網(wǎng)站網(wǎng)站建設(shè)網(wǎng)頁設(shè)計(jì)網(wǎng)站建設(shè)需要多少錢?
分布式鎖是一種用于控制分布式系統(tǒng)中資源訪問的同步機(jī)制,確保在任意時(shí)刻只有一個客戶端能夠獲取到鎖,并對共享資源進(jìn)行操作。
作用
1.保證數(shù)據(jù)一致性:在多個節(jié)點(diǎn)并發(fā)執(zhí)行的情況下,分布式鎖可以防止同時(shí)修改同一份數(shù)據(jù),從而避免數(shù)據(jù)不一致的問題。
2.協(xié)調(diào)任務(wù)執(zhí)行:確保特定的任務(wù)不會被重復(fù)執(zhí)行,特別是在需要冪等性(idempotent)保證的時(shí)候。
應(yīng)用場景例如
?庫存扣減:在電商系統(tǒng)中,當(dāng)用戶下單時(shí)需要扣減庫存,為了避免超賣現(xiàn)象,必須確保每次扣減操作都是原子性的。
?定時(shí)任務(wù)調(diào)度:在分布式環(huán)境中,確保同一個定時(shí)任務(wù)只在一個節(jié)點(diǎn)上運(yùn)行,防止重復(fù)執(zhí)行。
?緩存更新:當(dāng)多個服務(wù)實(shí)例試圖更新同一個緩存項(xiàng)時(shí),使用分布式鎖可以確保更新過程的線程安全。
?秒殺活動:對于高并發(fā)的搶購活動,如秒殺,使用分布式鎖來控制對有限商品資源的訪問是至關(guān)重要的。
?文件上傳:在分布式文件系統(tǒng)中,確保同一文件不會被多次上傳或覆蓋。
常見實(shí)現(xiàn)方式
?基于數(shù)據(jù)庫:可以使用數(shù)據(jù)庫的唯一索引來實(shí)現(xiàn)簡單的分布式鎖,也可以通過for update等機(jī)制來實(shí)現(xiàn)分布式鎖。例如,在嘗試獲取鎖時(shí)插入一條記錄,如果插入成功則表示獲取到鎖;如果違反了唯一索引約束,則說明鎖已經(jīng)被其他客戶端持有。這種方法簡單直接,但性能可能不如其他專門設(shè)計(jì)的解決方案,并且需要處理死鎖和鎖的自動釋放等問題。
?基于Redis:Redis是一個內(nèi)存中的鍵值存儲系統(tǒng),它提供了原子性的SETNX(Set if Not Exists)命令來設(shè)置一個鍵,只有當(dāng)該鍵不存在時(shí)才會成功。結(jié)合EXPIRE或PEXPIRE命令,可以為鎖設(shè)置一個過期時(shí)間,防止死鎖的發(fā)生。
?基于Zookeeper:Zookeeper支持臨時(shí)順序節(jié)點(diǎn),這使得它可以實(shí)現(xiàn)復(fù)雜的分布式鎖邏輯,如公平鎖、重入鎖以及讀寫鎖??蛻舳藙?chuàng)建一個臨時(shí)順序節(jié)點(diǎn)作為鎖對象,然后檢查自己創(chuàng)建的節(jié)點(diǎn)是否是最小編號的節(jié)點(diǎn),以此判斷是否獲得鎖。
?基于Etcd:Etcd是一個高可用的分布式鍵值存儲系統(tǒng),它也能夠提供分布式鎖功能。與Zookeeper類似,etcd使用臨時(shí)鍵和租約機(jī)制來實(shí)現(xiàn)鎖。
?基于Consul:同樣可以用來實(shí)現(xiàn)分布式鎖。Consul利用KV存儲和會話機(jī)制,可以方便地構(gòu)建出分布式鎖的應(yīng)用。
本文將利用raftx,用簡單的方法,編寫一個分布式鎖的應(yīng)用庫,它的特點(diǎn)是:
?使用方式簡單并且可用性強(qiáng)
?支持海量創(chuàng)建分布式鎖,可以同時(shí)創(chuàng)建幾十萬甚至上百萬個分布式鎖
?占用極少量的系統(tǒng)資源
?無自旋阻塞策略,不占用CPU資源
?搶占式獲取鎖
?支持TTL(time to live), 防止集群節(jié)點(diǎn)宕機(jī)造成死鎖
raftx的分布式易失性數(shù)據(jù)擴(kuò)展模塊實(shí)現(xiàn)分布式鎖 有比常見分布式鎖的實(shí)現(xiàn)較為明顯的特點(diǎn)
1.高效,它基于內(nèi)存。獲取與釋放分布式鎖過程更快
2.可以創(chuàng)建海量分布式鎖。如果系統(tǒng)需要創(chuàng)建海量分布式鎖,比如售票系統(tǒng),電商秒殺活動等, 對于Zookeeper,Etcd,redis等,在創(chuàng)建海量分布式鎖時(shí),可能面臨大量日志與大量觸發(fā)機(jī)制,導(dǎo)致系統(tǒng)負(fù)載過大的問題。而raftx則不會有這個問題??梢酝ㄟ^以下的Lockx的實(shí)現(xiàn)過程,詳細(xì)了解。
什么是Raftx
raftx 是一種對經(jīng)典 Raft 協(xié)議的擴(kuò)展,結(jié)合了 Multi-Paxos、ZAB(Zookeeper Atomic Broadcast)和 Raft 協(xié)議的優(yōu)勢。RaftX 具備快速選舉、并發(fā)提案、數(shù)據(jù)同步、數(shù)據(jù)回滾以及易失性數(shù)據(jù)同步等特性,適用于高并發(fā)和大規(guī)模分布式系統(tǒng)場景。
raftx wiki
Lockx 分布式鎖應(yīng)用庫,支持創(chuàng)建海量分布式鎖
Lockx是依賴raftx實(shí)現(xiàn)的一個分布式鎖應(yīng)用庫,實(shí)現(xiàn)方式簡單,代碼量少,100行左右代碼,但是它的功能卻十分強(qiáng)大,主要表現(xiàn)在:
?高效性與及時(shí)性
?資源占用極少
?支持海量創(chuàng)建分布式鎖
?API使用簡單方便
Lockx 支持一次性創(chuàng)建成千上萬,甚至數(shù)十萬或數(shù)百萬個分布式鎖,它的實(shí)現(xiàn)機(jī)制保證了它不會大量占用CPU資源和內(nèi)存資源;它的鎖動作變更觸發(fā)機(jī)制針對的是鎖資源,而非分布式對象鎖本身,也就是說,即使節(jié)點(diǎn)中有100萬個鎖競爭一個鎖資源,每次也只會觸發(fā)一次鎖的釋放與競爭的指令;比如鎖資源"lockmux",那么在分布式系統(tǒng)中,當(dāng)資源 “l(fā)ockmux”被釋放時(shí),它將觸發(fā)節(jié)點(diǎn)中的 “l(fā)ockmux”綁定事件一次,并讓等待的資源隨機(jī)發(fā)送一條競爭鎖的指令競爭該資源鎖,而不是觸發(fā)100萬個等待中的鎖對象競爭事件。
Lockx 實(shí)現(xiàn)方式
lockx主要依賴raftx的易失性數(shù)據(jù)API實(shí)現(xiàn),它的特點(diǎn)是高效,強(qiáng)一致性,并且可以綁定鍵值的增刪改的觸發(fā)事件;利用這些特性,可以輕松實(shí)現(xiàn)分布式鎖的邏輯。
m.raft.MemWatch([]byte(lockstr), func(key, value []byte, watchType raft.WatchType) {//獲取鎖成功與否if watchType == raft.ADD {if mb, ok := m.mp.Get(util.BytesToInt64(value)); ok {m.del(string(key), util.BytesToInt64(value))close(mb.ctx)}}//鎖釋放,阻塞代碼再次重新獲取分布式鎖if watchType == raft.DELETE {m.mux.Lock()defer m.mux.Unlock()if ids, b := m.rmap[string(key)]; b {for k := range ids {m.raft.MemCommand(key, util.Int64ToBytes(k), timeout, raft.MEM_PUT)break}}}//TryLock獲取鎖失敗觸發(fā)if watchType == raft.UPDATE {if mb, ok := m.mp.Get(util.BytesToInt64(value)); ok {if mb.isTry {m.del(string(key), util.BytesToInt64(value))mb.ctx <- trueclose(mb.ctx)}}}}, false, raft.ADD, raft.DELETE, raft.UPDATE)
這是lockx實(shí)現(xiàn)的核心代碼,主要通過監(jiān)聽raftx易失性數(shù)據(jù)主鍵的增刪改事件來實(shí)現(xiàn)資源鎖的鎖定與釋放
?raft.ADD 這是資源鎖新增的觸發(fā)事件,通過它判斷哪個對象獲取到分布式鎖,同時(shí)關(guān)閉相應(yīng)阻塞的通道,讓獲取鎖的程序繼續(xù)執(zhí)行。
?raft.DELETE 這是資源鎖刪除的觸發(fā)事件,同時(shí)它將再次發(fā)送獲取資源鎖的指令,搶占資源鎖
?raft.UPDATE 這是資源鎖更新的觸發(fā)事件,它表示資源鎖獲取失敗,用于TryLock,同時(shí)關(guān)閉相應(yīng)阻塞的通道并返回false
Lockx 使用方式
Lockx 的使用非常簡單,并且它可以支持大量創(chuàng)建分布式鎖,它一共有3個方法
?Lock(string,int)
獲取指定資源的分布式鎖并設(shè)置過期時(shí)間,阻塞
?TryLock(string,int)bool
獲取指定資源的分布式鎖并設(shè)置過期時(shí)間,若獲取不到返回false,不阻塞
?UnLock(string)
釋放指定資源的分布式鎖
以下模擬3個集群節(jié)點(diǎn)
//節(jié)點(diǎn)1,創(chuàng)建分布式鎖管理器 mutex1
mutex1 = NewMutex(":20001", []string{"127.0.0.1:20001", "127.0.0.1:20002", "127.0.0.1:20003"})//節(jié)點(diǎn)2,創(chuàng)建分布式鎖管理器 mutex2
mutex2 = NewMutex(":20002", []string{"127.0.0.1:20001", "127.0.0.1:20002", "127.0.0.1:20003"})//節(jié)點(diǎn)3,創(chuàng)建分布式鎖管理器 mutex3
mutex3 = NewMutex(":20003", []string{"127.0.0.1:20001", "127.0.0.1:20002", "127.0.0.1:20003"})
?第一個參數(shù) raftx服務(wù)地址
?第二個參數(shù)是所有集群節(jié)點(diǎn)都相同的,為所有節(jié)點(diǎn)的訪問地址 []string{"127.0.0.1:20001", "127.0.0.1:20002", "127.0.0.1:20003"}
這樣就完成了分布式鎖管理器的創(chuàng)建,并可以直接獲取各個自定義資源的分布式鎖,這里的資源指的是字符串,比如 “test”
示例
//節(jié)點(diǎn)1
func lock1(i int) {logger.Debugf("mutex1 lock%d lock.....", i)mutex1.Lock("test", 10)logger.Debugf("mutex1 lock%d get lock successful", i)time.Sleep(2 * time.Second)mutex1.Unlock("test")logger.Debugf("mutex1 lock%d unlock", i)
}//節(jié)點(diǎn)2
func lock2(i int) {logger.Debugf("mutex2 lock%d lock.....", i)mutex2.Lock("test", 10)logger.Debugf("mutex2 lock%d get lock successful", i)
}//節(jié)點(diǎn)3
func lock3(i int) {logger.Debugf("mutex3 lock%d lock.....", i)mutex3.Lock("test", 10)logger.Debugf("mutex3 lock%d get lock successful", i)time.Sleep(2 * time.Second)mutex3.Unlock("test")logger.Debugf("mutex3 lock%d unlock", i)
}
測試調(diào)用
func Test_lock(t *testing.T) {go lock1(1)go lock2(2)go lock3(3)select {}
}
執(zhí)行結(jié)果:
可以看到:
?2024/12/31 22:34:35
三個節(jié)點(diǎn)的同時(shí)搶占分布式鎖
?節(jié)點(diǎn)mutex2獲取到了鎖,由于mutex2沒有主動釋放鎖,mutex2.Lock("test", 10) 這里表示10秒后 ,集群自動釋放鎖
?2024/12/31 22:34:45
mutex2持有的分布式鎖被服務(wù)自動釋放,同時(shí)mutex1節(jié)點(diǎn)獲取到分布式鎖
?2024/12/31 22:34:47
mutex1在2秒后顯式調(diào)用UnLock釋放鎖,同時(shí)mutex3節(jié)點(diǎn)獲取到分布式鎖
?2024/12/31 22:34:49
mutex3在2秒后顯式調(diào)用UnLock釋放鎖
Lockx 可以海量創(chuàng)建分布式鎖,如:
func Test_multi_lock(t *testing.T) {for i := 1; i < 1<<15; i++ { //mutex1節(jié)點(diǎn)創(chuàng)建32768個并發(fā)任務(wù)go lock1(i)}for i := 1; i < 1<<15; i++ { //mutex2節(jié)點(diǎn)創(chuàng)建32768個并發(fā)任務(wù)go lock2(i)}for i := 1; i < 1<<15; i++ { //mutex3節(jié)點(diǎn)創(chuàng)建32768個并發(fā)任務(wù)go lock3(i)}select {}
}
?每個節(jié)點(diǎn)同時(shí)并發(fā)創(chuàng)建32768個分布式鎖對象
執(zhí)行結(jié)果:
可以看到,每2秒有一個對象獲取到分布式鎖,按順序依次執(zhí)行獲取分布式鎖與解鎖。
(注意:mutex2增加了2秒后釋放鎖,否則mutex2節(jié)點(diǎn)獲取鎖后,將等待10秒后有raftx集群釋放鎖)
Lockx 的源碼地址
可以直接將其當(dāng)成第三方分布式鎖庫在工程中使用
程序中調(diào)用示例
import "github.com/donnie4w/lockx"//創(chuàng)建分布式鎖管理器 mutex1
var mutex1 = lockx.NewMutex(":20001", []string{"127.0.0.1:20001", "127.0.0.1:20002", "127.0.0.1:20003"})
結(jié)論
Lockx
利用了 raftx
的高效特性和易失性數(shù)據(jù)存儲能力,提供了一種簡潔而強(qiáng)大的分布式鎖解決方案。它不僅適合常規(guī)的分布式鎖需求,還能夠在高并發(fā)環(huán)境下保持性能優(yōu)勢,確保系統(tǒng)的穩(wěn)定性和可靠性。
如果你考慮在項(xiàng)目中引入這樣的分布式鎖庫,可以參考上述信息進(jìn)行評估和集成。此外,也可以根據(jù)自己的具體需求調(diào)整 Lockx
的實(shí)現(xiàn),例如實(shí)現(xiàn)更復(fù)雜的鎖行為(如公平鎖等)。