分布式鎖的三種實現方式:
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...