現在的微服務系統,由於系統都是由多個微服務組成的,相比較於傳統的應用系統,鎖的實現就變的複雜了很多,單體應用的鎖也不適用與分布式系統,下面就介紹幾種分布式鎖的實現方式,常見的分布式鎖的實現方式有資料庫分布式鎖,redis分布式鎖,zookeeper分布式鎖;
一、資料庫分布式鎖:
1、利用資料庫記錄的唯一性實現分布式鎖:
新建一張表locks,method_name欄位代表要鎖住的方法名,他是乙個unique_key,當乙個執行緒要獲取方法上的鎖時,就像資料庫插入一條記錄,如果是第乙個獲取鎖時,因為資料庫沒有還沒有記錄,則返回插入記錄成功,獲取鎖成功,此時如果第二個執行緒去獲取鎖,做插入操作,由於資料庫裡面已經有一條記錄,所以插入失敗,獲取鎖失敗,此時便實現了分布式鎖:
create table locks(
id int not null auto_increment,
method_name varchar(100),
`desc` varchar(100),
create_time timestamp not null default current_timestamp,
modify_time timestamp not null default current_timestamp,
primary key (`id`),
unique key `udx_method_name`(`method_name`)
雖然用上面的資料庫記錄的唯一效能實現分布式鎖,但是存在幾個問題,我們還需要進行優化:
1.1、單節點的問題,單資料庫如果掛了怎麼辦?
答:建立2個資料庫,一主一從,從資料庫同步主資料庫的記錄,如果主資料掛了,切換到從資料庫;
1.2、非阻塞問題,現在是插入資料庫失敗(獲取鎖失敗),直接報錯,不是阻塞的;
答:程式裡面做乙個while迴圈,如果插入失敗,sleep一段時間再重試,此時的問題是要將異常處理當作業務來做,個人覺得不是很合適;
1.3、沒有失效時間;
答:資料庫增加乙個失效時間字段,啟動乙個執行緒單獨清理已失效的鎖記錄;
1.4、非重入鎖;
答:增加主機資訊和執行緒資訊、獲取鎖的次數(count),每次插入記錄之前先按主機資訊和執行緒資訊查詢表,如果有記錄,直接將鎖分配給他,同時將count加1;
2、利用mysql的排它鎖實現分布式鎖:
利用mysql的行鎖 for update實現分布式鎖;
select method_name from locks for update;
使用for update 行鎖後,其它需要獲取鎖的執行緒就需要等待手動的connection.commit()行鎖的釋放,所以此時是乙個阻塞鎖,但是無法解決非衝入和沒有失效時間的問題,同時還需要記住幾點:第一只有method_name是唯一索引的時候才會使用行鎖,否則會使用表鎖,還有一種情況就是由於表裡面資料較少,就算method_name是唯一索引,由於mysql底層的搜尋引擎的優化,也會出現不使用索引而走全表掃瞄的情況,第二就是如果鎖的數量特別大,大家都佔著行鎖不釋放的時候,可能導致資料庫連線池不夠用,資料庫宕機的情況;
二、快取分布式鎖:
這裡基於redis為例:
redis有乙個命令setnx命令,這裡的意思是如果key存在,則什麼都不操作返回0,如果key不存在,則插入key-value成功返回1,可以將method_name作為key,主機資訊和執行緒等資訊作為value,當插入成功返回1表示獲取鎖成功,當插入失敗返回0則表示獲取鎖失敗,由於現在的redis都是集群部署的,所以解決了單點登陸的問題,非阻塞問題也可以在程式裡面用while迴圈解決,像資料庫一樣,用value儲存主機資訊和執行緒資訊解決可重入問題,關於失效時間,redis可以為key設定失效時間,自動清理刪除已經失效的key,釋放鎖。(這裡有乙個問題,失效時間設定為多少合適呢?設定短了業務還沒執行完就釋放鎖會導致執行緒安全問題,設定時間長了會導致其他執行緒需要等待的時間過長,傷腦筋)但是快取分布式鎖的效率肯定要比資料庫分布式鎖高;
三、zookeeper實現分布式鎖:
zookeeper實現分布式鎖很簡單,就是在該方法對應指定節點的目錄下,建立乙個臨時有序節點,獲取鎖的方式:
第一步:在指定節點下面建立臨時有序節點,並返回該節點下所有的節點資訊;
第二步:如果自己是當前所有節點中最小的節點,則獲取鎖;
第三步:如果自己不是當前所有節點中最小的節點則查詢建立節點前面的乙個節點,並在前乙個節點上建立監視點(watch),當前乙個節點釋放鎖了,會刪除節點並觸發監視點,然後重新執行第二步;
由於zookeeper一般是集群部署的,所以不存在單點登陸的問題,zookeeper是乙個可阻塞的鎖的實現,通過新增監視點實現,重入問題同redis,失效時間問題:因為建立的是臨時有序節點,所以當客戶端與zookeeper斷開時,那麼這個臨時節點就會自動刪除(相當於釋放鎖),可能有人會問假入是網路抖動連線不上怎麼辦?這裡zookeeper有各種重連的策略,只有重連失敗後才會刪除節點,這裡就需要使用者根據自己的需求設定合適的重連策略;
三種方案對比:
從理解的難度上:
zookeeper > redis >資料庫(理解難度從難到易)
從分布式鎖的可靠性:
zookeeper > redis > 資料庫(可靠性從高到低)
從效能上排名:
redis > zook > 資料庫(效能由高到低)
分布式鎖實現的幾種方式
準備條件 建立表abc,並做唯一索引version 1 執行緒a加鎖 insert into abc version values versionid 2 執行緒a釋放鎖 delete from abc where version versionid 如果資料庫中有該鎖記錄,執行緒b再儲存資料進行加...
分布式鎖實現方式
1 資料庫的唯一索引實現 獲得鎖時向表中插入一條記錄,釋放鎖時刪除這條記錄。唯一索引可以保證該記錄只被插入一次,那麼就可以用這個記錄是否存在來判斷是否存於鎖定狀態。缺點 2 redis的setnx指令實現 使用 setnx set if not exist 指令插入乙個鍵值對,如果 key 已經存在...
分布式鎖的實現方式
在進行大型 技術架構設計以及業務實現的過程中,多少都會遇到需要使用分布式鎖的情況。那麼問題也就接踵而至。分布式鎖zk和memcached以及redis三者都能實現,同樣是分布式鎖,三者的區別何在?各自適用什麼場景?一 zookeeper 實現原理 基於zookeeper瞬時有序節點實現的分布式鎖,其...