一. 為什么要使用分布式鎖
Q: 在同一個(gè)JVM進(jìn)程內(nèi),多線程并發(fā)的情況下解虱,如果保證一個(gè)代碼塊在同一時(shí)間只能由一個(gè)線程訪問(wèn)?
A: 用【鎖】來(lái)保證悍汛,比如java的synchronized語(yǔ)法,以及Reentrantlock類等等
Q: 如果在分布式的集群環(huán)境中,如何保證不同節(jié)點(diǎn)的線程同步執(zhí)行呢术陶?
A: ?
???????為了保證一個(gè)方法或?qū)傩栽诟卟l(fā)情況下的同一時(shí)間只能被同一個(gè)線程執(zhí)行接谨,在傳統(tǒng)單體應(yīng)用單機(jī)部署的情況下,可以使用Java并發(fā)處理相關(guān)的API(如ReentrantLock或Synchronized)進(jìn)行互斥控制锅睛。在單機(jī)環(huán)境中辣垒,Java中提供了很多并發(fā)處理相關(guān)的API勋桶。但是,隨著業(yè)務(wù)發(fā)展的需要侥猬,原單體單機(jī)部署的系統(tǒng)被演化成分布式集群系統(tǒng)后例驹,由于分布式系統(tǒng)多線程、多進(jìn)程并且分布在不同機(jī)器上退唠,這將使原單機(jī)部署情況下的并發(fā)控制鎖策略失效鹃锈,單純的Java API并不能提供分布式鎖的能力。為了解決這個(gè)問(wèn)題就需要一種跨JVM的互斥機(jī)制來(lái)控制共享資源的訪問(wèn)瞧预,這就是分布式鎖要解決的問(wèn)題屎债!
??
二. 分布式鎖的特點(diǎn)
?????1.可以保證在分布式部署的應(yīng)用集群中,同一個(gè)方法在同一時(shí)間只能被一臺(tái)機(jī)器上的一個(gè)線程執(zhí)行垢油。
?????2.這把鎖要是一把可重入鎖(避免死鎖)
?????3.這把鎖最好是一把阻塞鎖(根據(jù)業(yè)務(wù)需要)
?????4.有高可用的獲取鎖和釋放鎖的功能
?????5.獲取鎖和釋放鎖的性能要好
??
三. 分布式鎖的三種實(shí)現(xiàn)
1.基于數(shù)據(jù)庫(kù)實(shí)現(xiàn)分布式鎖
?????要實(shí)現(xiàn)分布式鎖盆驹,最簡(jiǎn)單的方式可能就是直接創(chuàng)建一張鎖表,然后通過(guò)操作該表中的數(shù)據(jù)來(lái)實(shí)現(xiàn)了滩愁。
?????當(dāng)我們要鎖住某個(gè)方法或資源時(shí)躯喇,我們就在該表中增加一條記錄,想要釋放鎖的時(shí)候就刪除這條記錄硝枉。
?????創(chuàng)建這樣一張數(shù)據(jù)庫(kù)表:
CREATE TABLE `methodLock` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`method_name` varchar(64) NOT NULL DEFAULT '' COMMENT '鎖定的方法名',
`desc` varchar(1024) NOT NULL DEFAULT '備注信息',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '保存數(shù)據(jù)時(shí)間玖瘸,自動(dòng)生成',
PRIMARY KEY (`id`),
UNIQUE KEY `uidx_method_name` (`method_name `) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='鎖定中的方法';
當(dāng)我們想要鎖住某個(gè)方法時(shí),執(zhí)行以下SQL:
insert into methodLock(method_name,desc) values (‘method_name’,‘desc’)
?????因?yàn)槲覀儗?duì)method_name做了唯一性約束檀咙,這里如果有多個(gè)請(qǐng)求同時(shí)提交到數(shù)據(jù)庫(kù)的話雅倒,數(shù)據(jù)庫(kù)會(huì)保證只有一個(gè)操作可以成功,那么我們就可以認(rèn)為操作成功的那個(gè)線程獲得了該方法的鎖弧可,可以執(zhí)行方法體內(nèi)容蔑匣。
?????當(dāng)方法執(zhí)行完畢之后劣欢,想要釋放鎖的話,需要執(zhí)行以下Sql:
delete from methodLock where method_name ='method_name'
上面這種簡(jiǎn)單的實(shí)現(xiàn)有以下幾個(gè)問(wèn)題:
1裁良、這把鎖強(qiáng)依賴數(shù)據(jù)庫(kù)的可用性凿将,數(shù)據(jù)庫(kù)是一個(gè)單點(diǎn),一旦數(shù)據(jù)庫(kù)掛掉价脾,會(huì)導(dǎo)致業(yè)務(wù)系統(tǒng)不可用牧抵。
2、這把鎖沒有失效時(shí)間侨把,一旦解鎖操作失敗犀变,就會(huì)導(dǎo)致鎖記錄一直在數(shù)據(jù)庫(kù)中,其他線程無(wú)法再獲得到鎖秋柄。
3获枝、這把鎖只能是非阻塞的,因?yàn)閿?shù)據(jù)的insert操作骇笔,一旦插入失敗就會(huì)直接報(bào)錯(cuò)省店。沒有獲得鎖的線程并不會(huì)進(jìn)入排隊(duì)隊(duì)列,要想再次獲得鎖就要再次觸發(fā)獲得鎖操作笨触。
4懦傍、這把鎖是非重入的,同一個(gè)線程在沒有釋放鎖之前無(wú)法再次獲得該鎖芦劣。因?yàn)閿?shù)據(jù)中數(shù)據(jù)已經(jīng)存在了粗俱。
當(dāng)然,我們也可以有其他方式解決上面的問(wèn)題持寄。
1.數(shù)據(jù)庫(kù)是單點(diǎn)源梭?搞兩個(gè)數(shù)據(jù)庫(kù),數(shù)據(jù)之前雙向同步稍味。一旦掛掉快速切換到備庫(kù)上废麻。
2.沒有失效時(shí)間?只要做一個(gè)定時(shí)任務(wù)模庐,每隔一定時(shí)間把數(shù)據(jù)庫(kù)中的超時(shí)數(shù)據(jù)清理一遍掂碱。
3.非阻塞的沧卢?搞一個(gè)while循環(huán)披诗,直到insert成功再返回成功唱歧。
4.非重入的颅崩?在數(shù)據(jù)庫(kù)表中加個(gè)字段孩革,記錄當(dāng)前獲得鎖的機(jī)器的主機(jī)信息和線程信息锅移,那么下次再獲取鎖的時(shí)候先查詢數(shù)據(jù)庫(kù),如果當(dāng)前機(jī)器的主機(jī)信息和線程信息在數(shù)據(jù)庫(kù)可以查到的話,直接把鎖分配給他就可以了。
??
參考文章:
1.https://www.hollischuang.com/archives/1716
2.https://blog.csdn.net/xlgen157387/article/details/79036337
3.https://juejin.im/post/5b037d5c518825426e024473