分布式鎖的三種實現方式詳解

2021-09-07 18:04:07 字數 3830 閱讀 2109

分布式鎖的三種實現方式:

1、基於資料庫:1)利用唯一索引約束;2)利用資料庫自帶排他鎖

2、基於快取:利用setnx()返回值

3、基於zookeeper:1)利用zookeeper同乙個目錄下只能有乙個唯一檔名;2)利用zookeeper分布式鎖客戶端curator

注:需要考慮的因素:單點、可重入、阻塞、失效時間

基於資料庫的實現方式的核心思想是:在資料庫中建立乙個表,表中包含方法名等字段,並在方法名字段上建立唯一索引,想要執行某個方法,就使用這個方法名向表中插入資料,成功插入則獲取鎖,執行完成後刪除對應的行資料釋放鎖。

1、建立乙個表:

drop table if exists `method_lock`;

create table `method_lock` (

`id`

int(11) unsigned not null auto_increment comment '主鍵',

`method_name` varchar(64) not null comment '鎖定的方法名',

`desc` varchar(255) not null comment '備註資訊',

`update_time` timestamp not null default current_timestamp on update current_timestamp,

primary key (`id`),

unique key `uidx_method_name` (`method_name`) using btree

) engine=innodb auto_increment=3 default charset=utf8 comment='鎖定中的方法';

2、想要執行某個方法,就使用這個方法名向表中插入資料:

insert into method_lock (method_name, desc) values ('methodname', '測試的methodname');

因為我們對method_name做了唯一性約束,這裡如果有多個請求同時提交到資料庫的話,資料庫會保證只有乙個操作可以成功,那麼我們就可以認為操作成功的那個執行緒獲得了該方法的鎖,可以執行方法體內容。

3、成功插入則獲取鎖,執行完成後刪除對應的行資料釋放鎖:

delete from method_lock where method_name ='methodname';

注意:這只是使用基於資料庫的一種方法,使用資料庫實現分布式鎖還有很多其他的玩法!

使用基於資料庫的這種實現方式很簡單,但是對於分布式鎖應該具備的條件來說,它有一些問題需要解決及優化:

1)因為是基於資料庫實現的,資料庫的可用性和效能將直接影響分布式鎖的可用性及效能,所以,資料庫需要雙機部署、資料同步、主備切換;

2)不具備可重入的特性,因為同乙個執行緒在釋放鎖之前,行資料一直存在,無法再次成功插入資料,所以,需要在表中新增一列,用於記錄當前獲取到鎖的機器和執行緒資訊,在再次獲取鎖的時候,先查詢表中機器和執行緒資訊是否和當前機器和執行緒相同,若相同則直接獲取鎖;

3)沒有鎖失效機制,因為有可能出現成功插入資料後,伺服器宕機了,對應的資料沒有被刪除,當服務恢復後一直獲取不到鎖,所以,需要在表中新增一列,用於記錄失效時間,並且需要有定時任務清除這些失效的資料;

4)不具備阻塞鎖特性,獲取不到鎖直接返回失敗,所以需要優化獲取邏輯,迴圈多次去獲取。

5)在實施的過程中會遇到各種不同的問題,為了解決這些問題,實現方式將會越來越複雜;依賴資料庫需要一定的資源開銷,效能問題需要考慮。

1、選用redis實現分布式鎖原因:

1)redis有很高的效能;

2)redis命令對此支援較好,實現起來比較方便

2、使用命令介紹:

1)setnx

setnx key val:當且僅當key不存在時,set乙個key為val的字串,返回1;若key存在,則什麼都不做,返回0。

2)expire

expire key timeout:為key設定乙個超時時間,單位為second,超過這個時間鎖會自動釋放,避免死鎖。

3)delete

delete key:刪除key

3、實現思想:

1)獲取鎖的時候,使用setnx加鎖,並使用expire命令為鎖新增乙個超時時間,超過該時間則自動釋放鎖,鎖的value值為乙個隨機生成的uuid,通過此在釋放鎖的時候進行判斷。

2)獲取鎖的時候還設定乙個獲取的超時時間,若超過這個時間則放棄獲取鎖。

3)釋放鎖的時候,通過uuid判斷是不是該鎖,若是該鎖,則執行delete進行鎖釋放。

4、 分布式鎖的簡單實現**:

/**

* 分布式鎖的簡單實現** */

public

class

distributedlock

/*** 加鎖

* @param

lockname 鎖的key

* @param

acquiretimeout 獲取超時時間

* @param

timeout 鎖的超時時間

* @return

鎖標識

*/public string lockwithtimeout(string lockname, long acquiretimeout, long

timeout)

//返回-1代表key沒有設定超時時間,為key設定乙個超時時間

if (conn.ttl(lockkey) == -1)

try

catch

(interruptedexception e) }}

catch

(jedi***ception e)

finally

}return

retidentifier;

}/*** 釋放鎖

* @param

lockname 鎖的key

* @param

identifier 釋放鎖的標識

* @return

*/public

boolean

releaselock(string lockname, string identifier)

retflag = true

; }

conn.unwatch();

break

; }

} catch

(jedi***ception e)

finally

}return

retflag;

}}

zookeeper是乙個為分布式應用提供一致性服務的開源元件,它內部是乙個分層的檔案系統目錄樹結構,規定同乙個目錄下只能有乙個唯一檔名。基於zookeeper實現分布式鎖的步驟如下:

1)建立乙個目錄mylock;

2)執行緒a想獲取鎖就在mylock目錄下建立臨時順序節點;

3)獲取mylock目錄下所有的子節點,然後獲取比自己小的兄弟節點,如果不存在,則說明當前執行緒順序號最小,獲得鎖;

4)執行緒b獲取所有節點,判斷自己不是最小節點,設定監聽比自己次小的節點;

5)執行緒a處理完,刪除自己的節點,執行緒b監聽到變更事件,判斷自己是不是最小的節點,如果是則獲得鎖。

這裡推薦乙個apache的開源庫curator,它是乙個zookeeper客戶端,curator提供的interprocessmutex是分布式鎖的實現,acquire方法用於獲取鎖,release方法用於釋放鎖。

優點:具備高可用、可重入、阻塞鎖特性,可解決失效死鎖問題。

缺點:因為需要頻繁的建立和刪除節點,效能上不如redis方式。

實現分布式鎖的三種方式

1 資料庫的樂觀鎖 版本號機制 悲觀鎖與樂觀鎖 2 基於redis的分布式鎖 加鎖public class redistool return false 我們加鎖就一行 jedis.set string key,string value,string n string expx,int time 這...

實現分布式鎖的三種方式

類似的文章網上一搜一大把,實現方式也無非這三種,不過自己還是總結一下吧,實際應用中只採用過快取來實現 通過增刪操作,借助資料庫唯一索引的唯一性或主鍵唯一性,來實現 缺點 資料庫單點問題,如果資料庫掛了,會導致業務系統不可用 獲取鎖後,沒有失效時間,如果解鎖失敗,就會導致鎖記錄始終在資料庫中,其他執行...

分布式鎖的三種實現方式

更多請參考 1 基於資料庫的實現方式的核心思想是 在資料庫中建立乙個表,表中包含方法名等字段,並在方法名字段上建立唯一索引,想要執行某個方法,就使用這個方法名向表中插入資料,成功插入則獲取鎖,執行完成後刪除對應的行資料釋放鎖。drop table if exists method lock crea...