AVt天堂网 手机版,亚洲va久久久噜噜噜久久4399,天天综合亚洲色在线精品,亚洲一级Av无码毛片久久精品

當(dāng)前位置:首頁(yè) > 科技  > 軟件

深入理解分布式鎖:原理、應(yīng)用與挑戰(zhàn)

來(lái)源: 責(zé)編: 時(shí)間:2024-05-11 09:21:01 196觀(guān)看
導(dǎo)讀前言在單機(jī)環(huán)境中,我們主要通過(guò)線(xiàn)程間的加鎖機(jī)制來(lái)確保同一時(shí)間只有一個(gè)線(xiàn)程能夠訪(fǎng)問(wèn)某個(gè)共享資源或執(zhí)行某個(gè)關(guān)鍵代碼塊,從而防止各種并發(fā)修改異常。例如,在Java中提供了synchronized/Lock。但是在分布式環(huán)境中,這種線(xiàn)程

前言

在單機(jī)環(huán)境中,我們主要通過(guò)線(xiàn)程間的加鎖機(jī)制來(lái)確保同一時(shí)間只有一個(gè)線(xiàn)程能夠訪(fǎng)問(wèn)某個(gè)共享資源或執(zhí)行某個(gè)關(guān)鍵代碼塊,從而防止各種并發(fā)修改異常。例如,在Java中提供了synchronized/Lock。但是在分布式環(huán)境中,這種線(xiàn)程間的鎖機(jī)制已經(jīng)不起作用了,因?yàn)橄到y(tǒng)會(huì)被部署在不同機(jī)器上,這些資源已經(jīng)不是在線(xiàn)程間共享了,而是進(jìn)程之間共享資源。為了解決這個(gè)問(wèn)題,分布式鎖應(yīng)運(yùn)而生。本文將詳細(xì)解析分布式鎖的原理、應(yīng)用與挑戰(zhàn),以幫助讀者更好地理解和應(yīng)用分布式鎖。QmS28資訊網(wǎng)——每日最新資訊28at.com

分布式鎖的原理

首先,從最原始的鎖定義來(lái)看,鎖是一種同步機(jī)制,主要用于協(xié)調(diào)并發(fā)訪(fǎng)問(wèn)共享資源的行為。分布式鎖也符合這個(gè)定義,只不過(guò)運(yùn)行環(huán)境從單機(jī)變?yōu)榉植际江h(huán)境。它們的核心操作都可以分為以下三個(gè)步驟:QmS28資訊網(wǎng)——每日最新資訊28at.com

1. 獲取:在訪(fǎng)問(wèn)共享資源前,先獲取一個(gè)鎖QmS28資訊網(wǎng)——每日最新資訊28at.com

2. 占有:獲取成功的進(jìn)程或線(xiàn)程可以訪(fǎng)問(wèn)共享資源,其他進(jìn)程或線(xiàn)程則需要等待鎖釋放后才能進(jìn)行訪(fǎng)問(wèn)QmS28資訊網(wǎng)——每日最新資訊28at.com

3. 釋放:釋放鎖QmS28資訊網(wǎng)——每日最新資訊28at.com

同時(shí),分布式鎖也具備一般鎖的以下特性:QmS28資訊網(wǎng)——每日最新資訊28at.com

1. 互斥性:這是鎖的核心特性,確保在任意時(shí)刻,同一個(gè)鎖只能被一個(gè)進(jìn)程或線(xiàn)程所持有。這種特性對(duì)于確保資源的獨(dú)占訪(fǎng)問(wèn)和防止并發(fā)沖突至關(guān)重要。QmS28資訊網(wǎng)——每日最新資訊28at.com

2. 一致性:加鎖和釋放鎖的過(guò)程應(yīng)盡量由同一個(gè)線(xiàn)程或進(jìn)程完成,以確保鎖狀態(tài)的一致性,防止因鎖狀態(tài)不一致而導(dǎo)致的錯(cuò)誤或混亂。QmS28資訊網(wǎng)——每日最新資訊28at.com

3. 可重入性:這意味著已經(jīng)持有鎖的線(xiàn)程或進(jìn)程可以再次獲得同一個(gè)鎖,這在某些情況下是有用的,例如遞歸函數(shù)中的鎖操作。QmS28資訊網(wǎng)——每日最新資訊28at.com

還有分布式鎖的特性問(wèn)題:QmS28資訊網(wǎng)——每日最新資訊28at.com

4. 鎖租期問(wèn)題:在分布式鎖的場(chǎng)景中,為避免死鎖或無(wú)法正常釋放,鎖通常設(shè)置有效時(shí)間。當(dāng)有效時(shí)間過(guò)期但業(yè)務(wù)還在執(zhí)行時(shí),需要通過(guò)特定的機(jī)制(如watchdog)來(lái)續(xù)租,確保鎖的持有者能夠繼續(xù)完成其操作。QmS28資訊網(wǎng)——每日最新資訊28at.com

5. 性能:避免鎖成為分布式系統(tǒng)的瓶頸。QmS28資訊網(wǎng)——每日最新資訊28at.com

QmS28資訊網(wǎng)——每日最新資訊28at.com

分布式鎖的主流實(shí)現(xiàn)方案

常見(jiàn)的分布式鎖實(shí)現(xiàn)方案可以分為以下三大類(lèi):基于數(shù)據(jù)庫(kù)(比如MySQL),基于緩存(比如 Redis)和基于分布式一致性協(xié)調(diào)服務(wù)組件(比如 ZooKeeper、etcd)QmS28資訊網(wǎng)——每日最新資訊28at.com

QmS28資訊網(wǎng)——每日最新資訊28at.com

QmS28資訊網(wǎng)——每日最新資訊28at.com

基于數(shù)據(jù)庫(kù)的分布式鎖(以MySQL為例)

要實(shí)現(xiàn)一套基于數(shù)據(jù)庫(kù)的分布式鎖,最簡(jiǎn)單的方式可能就是直接創(chuàng)建一張鎖表,然后通過(guò)操作該表中的數(shù)據(jù)來(lái)實(shí)現(xiàn)分布式鎖。QmS28資訊網(wǎng)——每日最新資訊28at.com

為了更好的演示,我們先創(chuàng)建一張數(shù)據(jù)庫(kù)表,例如:QmS28資訊網(wǎng)——每日最新資訊28at.com

CREATE TABLE `database_lock` (  `id` bigint(20) NOT NULL AUTO_INCREMENT,  `resource` int(11) NOT NULL COMMENT '鎖定的資源',  `desc` varchar(128) NOT NULL DEFAULT '' COMMENT '描述',  `create_time` datetime COMMENT '創(chuàng)建時(shí)間',   `update_time` datetime COMMENT '更新時(shí)間'  PRIMARY KEY (`id`),  UNIQUE KEY `uniq_idx_resource` (`resource`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分布式鎖表';

記錄鎖

1. 獲取鎖:QmS28資訊網(wǎng)——每日最新資訊28at.com

當(dāng)想要獲取鎖時(shí),可以插入一條數(shù)據(jù):QmS28資訊網(wǎng)——每日最新資訊28at.com

INSERT INTO `database_lock` (resource, desc, create_time, update_time) VALUES (1,'lock',now(), now());

由于表中對(duì)resource設(shè)置了唯一索引,也就存在唯一性約束,這樣如果有多個(gè)請(qǐng)求同時(shí)提交到數(shù)據(jù)庫(kù)的話(huà),數(shù)據(jù)庫(kù)可以保證只有一個(gè)操作成功,那么我們就可以認(rèn)為操作成功的請(qǐng)求獲得了鎖。QmS28資訊網(wǎng)——每日最新資訊28at.com

2. 占有鎖:QmS28資訊網(wǎng)——每日最新資訊28at.com

成功獲取鎖后,就可以繼續(xù)操作共享資源了。QmS28資訊網(wǎng)——每日最新資訊28at.com

3. 釋放鎖:QmS28資訊網(wǎng)——每日最新資訊28at.com

當(dāng)需要釋放鎖時(shí),可以刪除這條數(shù)據(jù):QmS28資訊網(wǎng)——每日最新資訊28at.com

DELETE FROM database_lock WHERE resource = 1;

以上實(shí)現(xiàn)方式非常簡(jiǎn)單,但是以下幾點(diǎn)需要特別注意:QmS28資訊網(wǎng)——每日最新資訊28at.com

1. 這種鎖沒(méi)有失效時(shí)間,一旦釋放鎖的操作失敗就會(huì)導(dǎo)致鎖記錄一直存在數(shù)據(jù)庫(kù)中,鎖無(wú)法釋放,其他線(xiàn)程無(wú)法獲得鎖。這個(gè)缺陷也很好解決,比如可以增加一個(gè)定時(shí)任務(wù)定時(shí)清理未正常釋放的鎖記錄。QmS28資訊網(wǎng)——每日最新資訊28at.com

2. 這種鎖的可靠性依賴(lài)于數(shù)據(jù)庫(kù)。可以設(shè)置備庫(kù),避免單點(diǎn),進(jìn)一步提升可靠性。QmS28資訊網(wǎng)——每日最新資訊28at.com

3. 這種鎖時(shí)非阻塞的,因?yàn)椴迦霐?shù)據(jù)失敗后會(huì)立即報(bào)錯(cuò),想要獲得鎖就需要再次操作。如果需要阻塞式的,可以通過(guò)For循環(huán)、while循環(huán)模擬,直至成功再返回。QmS28資訊網(wǎng)——每日最新資訊28at.com

4. 這種鎖時(shí)非可重入的,因?yàn)橥粋€(gè)線(xiàn)程在沒(méi)有釋放鎖之前無(wú)法再次獲得鎖,因?yàn)閿?shù)據(jù)庫(kù)中已經(jīng)存在同一份記錄了。想要實(shí)現(xiàn)可重入,可以在數(shù)據(jù)庫(kù)中添加一些鎖的唯一標(biāo)識(shí)字段,比如 主機(jī)信息、線(xiàn)程信息等,那么再次獲取鎖的時(shí)候可以先查詢(xún)數(shù)據(jù),如果當(dāng)前的主機(jī)信息和線(xiàn)程信息等能被查詢(xún)到的話(huà),可以直接分配鎖。QmS28資訊網(wǎng)——每日最新資訊28at.com

樂(lè)觀(guān)鎖

如果數(shù)據(jù)的更新在大多數(shù)情況下是不會(huì)產(chǎn)生沖突的,那么只在數(shù)據(jù)庫(kù)更新操作提交的時(shí)候?qū)?shù)據(jù)作沖突檢測(cè),如果檢測(cè)的結(jié)果與預(yù)期一致,則獲得鎖,如果出現(xiàn)了與預(yù)期數(shù)據(jù)不一致的情況,則丟棄本次更新。QmS28資訊網(wǎng)——每日最新資訊28at.com

樂(lè)觀(guān)鎖大多數(shù)是基于版本控制實(shí)現(xiàn)的。即給數(shù)據(jù)增加一個(gè)版本標(biāo)識(shí),比如通過(guò)為數(shù)據(jù)庫(kù)表添加一個(gè)"version"字段來(lái)實(shí)現(xiàn)。QmS28資訊網(wǎng)——每日最新資訊28at.com

為了更好的理解數(shù)據(jù)庫(kù)樂(lè)觀(guān)鎖在實(shí)際項(xiàng)目中的使用,這里就列舉一個(gè)典型的電商庫(kù)存更新的例子。電商平臺(tái)中,當(dāng)用戶(hù)提單的時(shí)候就會(huì)對(duì)庫(kù)存進(jìn)行操作(庫(kù)存減1代表已經(jīng)賣(mài)出了一件)。我們將這個(gè)庫(kù)存模型用下面的一張表optimistic_lock來(lái)表述:QmS28資訊網(wǎng)——每日最新資訊28at.com

CREATE TABLE `optimistic_lock` ( `id` BIGINT NOT NULL AUTO_INCREMENT, `resource` int NOT NULL COMMENT '鎖定的資源', `version` int NOT NULL COMMENT '鎖的版本信息', `create_time` datetime COMMENT '創(chuàng)建時(shí)間', `update_time` datetime COMMENT '更新時(shí)間', `delete_time` datetime COMMENT '刪除時(shí)間',  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分布式鎖表-樂(lè)觀(guān)鎖';

其中:resource表示具體操作的資源,在這里也就是特指庫(kù)存;version表示版本號(hào)。QmS28資訊網(wǎng)——每日最新資訊28at.com

在使用樂(lè)觀(guān)鎖之前要確保表中有相應(yīng)的數(shù)據(jù),比如:QmS28資訊網(wǎng)——每日最新資訊28at.com

INSERT INTO optimistic_lock (resource, version, create_at, update_at) VALUES(20, 10, now(), now());

如果只有一個(gè)線(xiàn)程進(jìn)行操作,數(shù)據(jù)庫(kù)本身就能保證操作的正確性。主要步驟如下:QmS28資訊網(wǎng)——每日最新資訊28at.com

1. 獲取資源信息:SELECT resource FROM optimistic_lock WHERE id = 1QmS28資訊網(wǎng)——每日最新資訊28at.com

2. 執(zhí)行業(yè)務(wù)邏輯QmS28資訊網(wǎng)——每日最新資訊28at.com

3. 提交數(shù)據(jù):UPDATE optimistic_lock SET resource = resource -1 WHERE id = 1QmS28資訊網(wǎng)——每日最新資訊28at.com

但是當(dāng)有兩個(gè)用戶(hù)同時(shí)購(gòu)買(mǎi)一件商品時(shí),庫(kù)存實(shí)際操作應(yīng)該是庫(kù)存(resource)減2,但是由于有高并發(fā)的存在,第一個(gè)用戶(hù)請(qǐng)求執(zhí)行之后(執(zhí)行了1、2,但是還沒(méi)有完成3),第二個(gè)用戶(hù)在購(gòu)買(mǎi)相同的商品(執(zhí)行1),此時(shí)查詢(xún)出的庫(kù)存并沒(méi)有完成減1的動(dòng)作,那么最終會(huì)導(dǎo)致2個(gè)線(xiàn)程購(gòu)買(mǎi)的商品卻出現(xiàn)庫(kù)存只減1的情況,最終導(dǎo)致庫(kù)存異常。QmS28資訊網(wǎng)——每日最新資訊28at.com

在引入了version版本控制之后,具體的操作就會(huì)演變成如下步驟:QmS28資訊網(wǎng)——每日最新資訊28at.com

1. 獲取資源信息: SELECT resource, version as oldVersion FROM optimistic_lock WHERE id = 1QmS28資訊網(wǎng)——每日最新資訊28at.com

2. 執(zhí)行業(yè)務(wù)邏輯QmS28資訊網(wǎng)——每日最新資訊28at.com

3. 更新資源:UPDATE optimistic_lock SET resource = resource -1, version = version + 1 WHERE id = 1 AND version = oldVersionQmS28資訊網(wǎng)——每日最新資訊28at.com

另外,借助更新時(shí)間戳(update_at)也可以實(shí)現(xiàn)樂(lè)觀(guān)鎖,和采用version字段的方式相似:更新操作執(zhí)行前先獲取并記錄當(dāng)前的更新時(shí)間,在提交更新時(shí),檢測(cè)當(dāng)前更新時(shí)間是否與更新開(kāi)始時(shí)獲取的更新時(shí)間戳相等。QmS28資訊網(wǎng)——每日最新資訊28at.com

由于在檢測(cè)數(shù)據(jù)沖突時(shí)并不依賴(lài)唯一索引,不會(huì)影響請(qǐng)求的性能,在并發(fā)量較小的時(shí)候只有少部分請(qǐng)求會(huì)失敗,適用于競(jìng)爭(zhēng)較少的場(chǎng)景。缺點(diǎn)是當(dāng)應(yīng)用并發(fā)量高的時(shí)候,version值在頻繁變化,則會(huì)導(dǎo)致大量請(qǐng)求失敗,影響系統(tǒng)的可用性。另外,我們通過(guò)上述sql語(yǔ)句還可以看到,數(shù)據(jù)庫(kù)鎖都是作用于同一行數(shù)據(jù)記錄上,這就會(huì)導(dǎo)致熱點(diǎn)數(shù)據(jù),在一些特殊場(chǎng)景,如大促、秒殺等活動(dòng)的時(shí)候,大量的請(qǐng)求同時(shí)請(qǐng)求同一條記錄的行鎖,會(huì)對(duì)數(shù)據(jù)庫(kù)產(chǎn)生很大的寫(xiě)壓力。所以綜合數(shù)據(jù)庫(kù)樂(lè)觀(guān)鎖的優(yōu)缺點(diǎn),可以看出樂(lè)觀(guān)鎖比較適合并發(fā)量不高,寫(xiě)操作不頻繁的場(chǎng)景。QmS28資訊網(wǎng)——每日最新資訊28at.com

悲觀(guān)鎖

我們還可以借助數(shù)據(jù)庫(kù)中自帶的鎖來(lái)實(shí)現(xiàn)分布式鎖。例如在查詢(xún)語(yǔ)句后面增加FOR UPDATE,數(shù)據(jù)庫(kù)會(huì)在查詢(xún)過(guò)程中給數(shù)據(jù)庫(kù)表增加悲觀(guān)鎖,也稱(chēng)排他鎖。當(dāng)某條記錄被加上悲觀(guān)鎖之后,其它線(xiàn)程也就無(wú)法再該行上增加悲觀(guān)鎖。QmS28資訊網(wǎng)——每日最新資訊28at.com

悲觀(guān)鎖,與樂(lè)觀(guān)鎖相反,總是假設(shè)最壞的情況,它認(rèn)為數(shù)據(jù)的更新在大多數(shù)情況下是會(huì)產(chǎn)生沖突的。QmS28資訊網(wǎng)——每日最新資訊28at.com

在使用悲觀(guān)鎖的同時(shí),我們需要注意一下鎖的級(jí)別。MySQL InnoDB引擎在加鎖的時(shí)候,只有明確地指定主鍵(或唯一索引)的才會(huì)執(zhí)行行鎖 (只鎖住被選取的數(shù)據(jù))。 在使用悲觀(guān)鎖時(shí),我們必須關(guān)閉MySQL數(shù)據(jù)庫(kù)的自動(dòng)提交屬性(參考下面的示例),因?yàn)镸ySQL默認(rèn)使用autocommit模式,也就是說(shuō),當(dāng)你執(zhí)行一個(gè)更新操作后,MySQL會(huì)立刻將結(jié)果進(jìn)行提交。QmS28資訊網(wǎng)——每日最新資訊28at.com

mysql> SET AUTOCOMMIT = 0;Query OK, 0 rows affected (0.00 sec)

這樣在使用FOR UPDATE獲得鎖之后可以執(zhí)行相應(yīng)的業(yè)務(wù)邏輯,執(zhí)行完之后再使用COMMIT來(lái)釋放鎖。QmS28資訊網(wǎng)——每日最新資訊28at.com

下面通過(guò)前面的database_lock表來(lái)具體表述一下用法。假設(shè)有一線(xiàn)程A需要獲得鎖并執(zhí)行相應(yīng)的操作,那么它的具體步驟如下:QmS28資訊網(wǎng)——每日最新資訊28at.com

1. 獲取鎖:SELECT * FROM database_lock WHERE id = 1 FOR UPDATE;。QmS28資訊網(wǎng)——每日最新資訊28at.com

2. 執(zhí)行業(yè)務(wù)邏輯。QmS28資訊網(wǎng)——每日最新資訊28at.com

3. 釋放鎖:COMMIT。QmS28資訊網(wǎng)——每日最新資訊28at.com

如果另一個(gè)線(xiàn)程B在線(xiàn)程A釋放鎖之前執(zhí)行步驟1,那么它會(huì)被阻塞,直至線(xiàn)程A釋放鎖之后才能繼續(xù)。注意,如果線(xiàn)程A長(zhǎng)時(shí)間未釋放鎖,那么線(xiàn)程B會(huì)報(bào)錯(cuò),參考如下(lock wait time可以通過(guò)innodb_lock_wait_timeout來(lái)進(jìn)行配置):QmS28資訊網(wǎng)——每日最新資訊28at.com

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

注意事項(xiàng):QmS28資訊網(wǎng)——每日最新資訊28at.com

1. 上面的示例中演示了指定主鍵并且能查詢(xún)到數(shù)據(jù)的過(guò)程(觸發(fā)行鎖),如果查不到數(shù)據(jù)那么也就無(wú)從“鎖”起了。 2. 如果未指定主鍵(或者唯一索引)且能查詢(xún)到數(shù)據(jù),那么就會(huì)觸發(fā)表鎖或間隙鎖,比如步驟1改為執(zhí)行:QmS28資訊網(wǎng)——每日最新資訊28at.com

SELECT * FROM database_lock WHERE desc='lock' FOR UPDATE;

或者主鍵不明確也會(huì)觸發(fā)表鎖,又比如步驟1改為執(zhí)行:QmS28資訊網(wǎng)——每日最新資訊28at.com

SELECT * FROM database_lock WHERE id>0 FOR UPDATE;

在悲觀(guān)鎖中,每一次行數(shù)據(jù)的訪(fǎng)問(wèn)都是獨(dú)占的,只有當(dāng)正在訪(fǎng)問(wèn)該行數(shù)據(jù)的請(qǐng)求事務(wù)提交以后,其他請(qǐng)求才能依次訪(fǎng)問(wèn)該數(shù)據(jù),否則將阻塞等待鎖的獲取。悲觀(guān)鎖可以嚴(yán)格保證數(shù)據(jù)訪(fǎng)問(wèn)的安全。但是缺點(diǎn)也明顯,即每次請(qǐng)求都會(huì)額外產(chǎn)生加鎖的開(kāi)銷(xiāo)且未獲取到鎖的請(qǐng)求將會(huì)阻塞等待鎖的獲取,在高并發(fā)環(huán)境下,容易造成大量請(qǐng)求阻塞,影響系統(tǒng)性能。另外,悲觀(guān)鎖使用不當(dāng)還可能產(chǎn)生死鎖的情況。QmS28資訊網(wǎng)——每日最新資訊28at.com

小結(jié)

基于以上討論,借助與數(shù)據(jù)庫(kù)自身的能力(唯一索引,數(shù)據(jù)庫(kù)排他鎖),基于數(shù)據(jù)庫(kù)實(shí)現(xiàn)分布式鎖還是挺簡(jiǎn)單的。下面對(duì)其實(shí)用性其進(jìn)行簡(jiǎn)單分析:QmS28資訊網(wǎng)——每日最新資訊28at.com

優(yōu)點(diǎn):QmS28資訊網(wǎng)——每日最新資訊28at.com

?實(shí)現(xiàn)簡(jiǎn)單,容易理解,不需要額外的第三方中間件。QmS28資訊網(wǎng)——每日最新資訊28at.com

?通過(guò)數(shù)據(jù)庫(kù)的事務(wù)特性可以確保鎖的原子性、互斥性。QmS28資訊網(wǎng)——每日最新資訊28at.com

不足:QmS28資訊網(wǎng)——每日最新資訊28at.com

?性能相對(duì)較低,特別是在高并發(fā)場(chǎng)景下,頻繁的數(shù)據(jù)庫(kù)操作可能導(dǎo)致性能瓶頸。QmS28資訊網(wǎng)——每日最新資訊28at.com

?需要自己考慮鎖超時(shí)等問(wèn)題,實(shí)現(xiàn)起來(lái)較為繁瑣。QmS28資訊網(wǎng)——每日最新資訊28at.com

?依賴(lài)本地事務(wù),不支持集群部署,不能保證高可用。QmS28資訊網(wǎng)——每日最新資訊28at.com

基于Redis實(shí)現(xiàn)的分布式鎖

方案一:SETNX+EXPIRE

這種是最簡(jiǎn)單的實(shí)現(xiàn)方式,先通過(guò)setNX或取到鎖,然后通過(guò)expire命令添加超時(shí)時(shí)間。這種方式存在一個(gè)很大的問(wèn)題:這兩個(gè)命令不是原子操作,需要和redis交互兩次,客戶(hù)端可能會(huì)在第一個(gè)命令執(zhí)行完之后掛掉,導(dǎo)致沒(méi)有設(shè)置超時(shí)時(shí)間,鎖無(wú)法正常失效。于是產(chǎn)生了以下優(yōu)化方案。QmS28資訊網(wǎng)——每日最新資訊28at.com

方案二:SETNX+VALUE

這種方式的value值中保存的是客戶(hù)端計(jì)算出的過(guò)期時(shí)間,通過(guò)setnx命令一次性寫(xiě)入redis中QmS28資訊網(wǎng)——每日最新資訊28at.com

public boolean getLock(String key,Long expireTime) {    long now = System.currentTimeMills();    //絕對(duì)超時(shí)時(shí)間    long expireTime = now + expireTime;     String expiresStr = String.valueOf(expireTime);     // 加鎖成功     if ( jedis.setnx(key, expiresStr)==1) {         return true;     }     // 檢查鎖是否過(guò)期,獲取鎖的value     String currentValueStr = jedis.get(key);     // 如果記錄的過(guò)期時(shí)間小于系統(tǒng)時(shí)間,則表示已過(guò)期     if (currentValueStr != null && Long.parseLong(currentValueStr) < now) {         // 鎖已過(guò)期,獲取上一個(gè)鎖的過(guò)期時(shí)間,并設(shè)置現(xiàn)在鎖的過(guò)期時(shí)間         String oldValueStr = jedis.getSet(key, expiresStr);         if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {             // 考慮多線(xiàn)程并發(fā)的情況,只有一個(gè)線(xiàn)程的設(shè)置值和當(dāng)前值相同,它才可以加鎖             return true;         }     }     //其他情況,均返回加鎖失敗     return false;}

這種方式通過(guò)value將超時(shí)時(shí)間賦值,解決了第一種方案的兩次操作不能保證原子性的問(wèn)題。但是這種方式也有問(wèn)題:QmS28資訊網(wǎng)——每日最新資訊28at.com

1. 在鎖過(guò)期時(shí),如果多個(gè)線(xiàn)程同時(shí)來(lái)加鎖,可能會(huì)導(dǎo)致多個(gè)線(xiàn)程都加鎖成功(不滿(mǎn)足互斥性);QmS28資訊網(wǎng)——每日最新資訊28at.com

2. 在多個(gè)線(xiàn)程都加鎖成功后,因?yàn)殒i中沒(méi)有加鎖線(xiàn)程的標(biāo)識(shí),會(huì)導(dǎo)致多個(gè)線(xiàn)程都可以解鎖(不滿(mǎn)足一致性);QmS28資訊網(wǎng)——每日最新資訊28at.com

3. 超時(shí)時(shí)間是在客戶(hù)端計(jì)算的,不同的客戶(hù)端的時(shí)鐘可能會(huì)存在差異,導(dǎo)致在加鎖客戶(hù)端沒(méi)有超時(shí)的鎖,在另一個(gè)客戶(hù)端已經(jīng)超時(shí)(基于客戶(hù)端時(shí)鐘,不滿(mǎn)足一致性)。QmS28資訊網(wǎng)——每日最新資訊28at.com

方案三:使用Lua腳本

同樣是為了解決第一種方案中的原子性問(wèn)題,我們可以采用Lua腳本,來(lái)保證SETNX+EXPIRE操作的原子性。QmS28資訊網(wǎng)——每日最新資訊28at.com

if redis.call('setnx',KEYS[1],ARGV[1]) == 1 then     redis.call('expire',KEYS[1],ARGV[2])else    return 0end;

在Java代碼中,使用jedis.eval()執(zhí)行加鎖。QmS28資訊網(wǎng)——每日最新資訊28at.com

public boolean getLock(String key, String value, long expireTime) {      String lua_scripts = "if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then " +                            "redis.call('expire', KEYS[1], ARGV[2]) " +                            "return 1 " +                            "else " +                            "return 0 " +                            "end";      List<String> keys = Collections.singletonList(key);      List<String> argv = Arrays.asList(value, String.valueOf(expireTime));      Long result = (Long) jedis.eval(lua_scripts, keys, argv);      return result != null && result == 1;  }

這種方式可以完全避免在加鎖后中斷設(shè)置不上超時(shí)時(shí)間的問(wèn)題。也不會(huì)存在有時(shí)鐘不一致的問(wèn)題,和高并發(fā)情況下多個(gè)線(xiàn)程都加上鎖的問(wèn)題。但是這種方式就一定沒(méi)有問(wèn)題了嗎?答案是否定的。考慮以下場(chǎng)景:QmS28資訊網(wǎng)——每日最新資訊28at.com

當(dāng)服務(wù)A加鎖成功后,正在執(zhí)行業(yè)務(wù)的過(guò)程中,鎖過(guò)期啦,這時(shí)服務(wù)A是沒(méi)有感知的;QmS28資訊網(wǎng)——每日最新資訊28at.com

接著服務(wù)B這時(shí)來(lái)獲取鎖,成功獲取到了;QmS28資訊網(wǎng)——每日最新資訊28at.com

緊接著,服務(wù)A處理完業(yè)務(wù)了,來(lái)釋放鎖,成功釋放掉了,而服務(wù)B這時(shí)還以為它的鎖還在,在執(zhí)行代碼。QmS28資訊網(wǎng)——每日最新資訊28at.com

全亂套了有沒(méi)有?以為自己加鎖了,其實(shí)你沒(méi)加;QmS28資訊網(wǎng)——每日最新資訊28at.com

以為自己解鎖成功了,其實(shí)解的是別人的鎖;QmS28資訊網(wǎng)——每日最新資訊28at.com

這種方案的問(wèn)題主要是因?yàn)閮牲c(diǎn):鎖過(guò)期釋放,業(yè)務(wù)沒(méi)處理完;鎖沒(méi)有唯一身份標(biāo)識(shí)。QmS28資訊網(wǎng)——每日最新資訊28at.com

備注:從Redis 2.6.12版本開(kāi)始支持setNx同時(shí)設(shè)置超時(shí)時(shí)間QmS28資訊網(wǎng)——每日最新資訊28at.com

如果你想要在設(shè)置key的同時(shí)為其設(shè)置過(guò)期時(shí)間,并希望這是一個(gè)原子操作,你可以考慮使用Redis的 SET 命令,如下所示:QmS28資訊網(wǎng)——每日最新資訊28at.com

SET mykey "myvalue" NX EX 10  # 設(shè)置mykey的值為myvalue,僅當(dāng)mykey不存在時(shí),并設(shè)置過(guò)期時(shí)間為10秒

方案四:SET NX PX EX + 唯一標(biāo)識(shí)

對(duì)于誤刪鎖的問(wèn)題,我們可以在加鎖時(shí),由客戶(hù)端生成一個(gè)唯一ID作為value設(shè)置在鎖中,在刪除鎖時(shí)先進(jìn)行身份判斷,再刪除;加鎖邏輯如下:QmS28資訊網(wǎng)——每日最新資訊28at.com

public boolean getLock(String key,String uniId,Long expireTime) {        //加鎖        return jedis.set(key, uniId, "NX", "EX", expireTime) == 1;}// 解鎖public boolean releaseLock(String key,String uniId) {        // 因?yàn)間et和del操作并不是原子的,所以使用lua腳本        String lua_script = "if redis.call('get',KEYS[1]) == ARGV[1] then  return redis.call('del',KEYS[1]) else return 0  end;";     List<String> keys = Collections.singletonList(key);     List<String> argv = Arrays.asList(uuiId);      Object result = jedis.eval(lua_scripts, keys, argv);        return result !=null && result.equals(1L);}

這種方式解決了鎖被誤刪的問(wèn)題,但是同樣存在鎖超時(shí)失效,但是業(yè)務(wù)還未處理完的問(wèn)題。QmS28資訊網(wǎng)——每日最新資訊28at.com

方案五:Redission框架

那么對(duì)于鎖過(guò)期失效,業(yè)務(wù)未處理完畢的問(wèn)題,該如何處理呢?QmS28資訊網(wǎng)——每日最新資訊28at.com

我們可以在加鎖成功后,啟動(dòng)一個(gè)守護(hù)線(xiàn)程,在守護(hù)線(xiàn)程中隔一段時(shí)間就對(duì)鎖的超時(shí)時(shí)間再續(xù)長(zhǎng)一點(diǎn),直到業(yè)務(wù)處理完成后再釋放鎖,防止鎖在業(yè)務(wù)處理完畢之前提前釋放。而Redission框架就是使用的這種機(jī)制來(lái)解決的這個(gè)問(wèn)題。QmS28資訊網(wǎng)——每日最新資訊28at.com

1. 當(dāng)一個(gè)線(xiàn)程去獲取鎖,在加鎖成功的情況下,那么它已經(jīng)通過(guò)Lua腳本將數(shù)據(jù)保存在了redis中;QmS28資訊網(wǎng)——每日最新資訊28at.com

2. 然后在加鎖成功的同時(shí),啟動(dòng)Watch Dog看門(mén)狗,每隔10秒檢查是否還持有鎖,如果是則將鎖超時(shí)時(shí)間延長(zhǎng)。QmS28資訊網(wǎng)——每日最新資訊28at.com

3. 如果一開(kāi)始就獲取鎖失敗,則會(huì)一直循環(huán)獲取。QmS28資訊網(wǎng)——每日最新資訊28at.com

方案六:RedLock

以上的這些方案,都只是在Redis單機(jī)模式下討論的方案,如果Redis是采用集群模式,還會(huì)存在一些問(wèn)題,比如:QmS28資訊網(wǎng)——每日最新資訊28at.com

在集群模式下,一般Master節(jié)點(diǎn)會(huì)將數(shù)據(jù)同步到Salve節(jié)點(diǎn),如果我們先在Master節(jié)點(diǎn)上加鎖成功,在同步到Salve節(jié)點(diǎn)之前,這個(gè)Master節(jié)點(diǎn)掛了,然后另一臺(tái)Salve節(jié)點(diǎn)升級(jí)為Master節(jié)點(diǎn),這時(shí)這個(gè)節(jié)點(diǎn)上并沒(méi)有我們的加鎖數(shù)據(jù);QmS28資訊網(wǎng)——每日最新資訊28at.com

此時(shí)另一個(gè)客戶(hù)端線(xiàn)程來(lái)獲取相同的鎖,它就會(huì)獲取成功,這時(shí)在我們的應(yīng)用中將會(huì)有兩個(gè)線(xiàn)程同時(shí)獲取到這個(gè)鎖,這個(gè)鎖也就不安全了。QmS28資訊網(wǎng)——每日最新資訊28at.com

為了解決這個(gè)問(wèn)題,Redis的作者提出了一種高級(jí)的分布式鎖算法,叫:RedLock,即:Redis Distributed Lock, Redis分布式鎖。QmS28資訊網(wǎng)——每日最新資訊28at.com

RedLock的核心原理:QmS28資訊網(wǎng)——每日最新資訊28at.com

?在Redis集群中選出多個(gè)Master節(jié)點(diǎn),保證這些Master節(jié)點(diǎn)不會(huì)同時(shí)宕機(jī);QmS28資訊網(wǎng)——每日最新資訊28at.com

?并且各個(gè)Master節(jié)點(diǎn)之間相互獨(dú)立,數(shù)據(jù)不同步;QmS28資訊網(wǎng)——每日最新資訊28at.com

?使用與Redis單實(shí)例相同的方法來(lái)加鎖和解鎖。QmS28資訊網(wǎng)——每日最新資訊28at.com

那么RedLock到底是如何來(lái)保證在有節(jié)點(diǎn)宕機(jī)的情況下,還能安全的呢?QmS28資訊網(wǎng)——每日最新資訊28at.com

1.假設(shè)集群中有N臺(tái)Master節(jié)點(diǎn),首先,獲取當(dāng)前時(shí)間戳;QmS28資訊網(wǎng)——每日最新資訊28at.com

2.客戶(hù)端按照順序使用相同的key,value依次獲取鎖,并且獲取時(shí)間要比鎖超時(shí)時(shí)間足夠小;比如超時(shí)時(shí)間5s,那么獲取鎖時(shí)間最多1s,超過(guò)1s則放棄,繼續(xù)獲取下一個(gè);QmS28資訊網(wǎng)——每日最新資訊28at.com

3.客戶(hù)端通過(guò)獲取所有能獲取的鎖之后減去第一步的時(shí)間戳,這個(gè)時(shí)間差要小于鎖超時(shí)時(shí)間,并且要至少有N/2 + 1臺(tái)節(jié)點(diǎn)獲取成功,才表示鎖獲取成功,否則獲取失敗;QmS28資訊網(wǎng)——每日最新資訊28at.com

4.如果成功獲取鎖,則鎖的有效時(shí)間是原本超時(shí)時(shí)間減去第三步的時(shí)間差;QmS28資訊網(wǎng)——每日最新資訊28at.com

5.如果獲取鎖失敗,則要解鎖所有的節(jié)點(diǎn),不管該節(jié)點(diǎn)加鎖時(shí)是否成功,防止有漏網(wǎng)之魚(yú)。QmS28資訊網(wǎng)——每日最新資訊28at.com

Redssion庫(kù)對(duì)RedLock方案已經(jīng)做了實(shí)現(xiàn),如果你的Redis是集群部署,可以看看使用方法。QmS28資訊網(wǎng)——每日最新資訊28at.com

參考文檔:https://redis.io/topics/distlockQmS28資訊網(wǎng)——每日最新資訊28at.com

小結(jié)

優(yōu)點(diǎn):QmS28資訊網(wǎng)——每日最新資訊28at.com

?實(shí)現(xiàn)簡(jiǎn)單,性能較高。QmS28資訊網(wǎng)——每日最新資訊28at.com

?可以利用Redis的集群特性實(shí)現(xiàn)高可用性和可擴(kuò)展性。QmS28資訊網(wǎng)——每日最新資訊28at.com

?有現(xiàn)成的第三方包和工具支持,實(shí)現(xiàn)起來(lái)相對(duì)簡(jiǎn)單。QmS28資訊網(wǎng)——每日最新資訊28at.com

缺點(diǎn):QmS28資訊網(wǎng)——每日最新資訊28at.com

?如果Redis節(jié)點(diǎn)故障,可能導(dǎo)致鎖失效或死鎖。QmS28資訊網(wǎng)——每日最新資訊28at.com

?RedLock算法雖然提高了容錯(cuò)性,但增加了實(shí)現(xiàn)的復(fù)雜性和開(kāi)銷(xiāo)。QmS28資訊網(wǎng)——每日最新資訊28at.com

基于Zookeeper等實(shí)現(xiàn)的分布式鎖

zookeeper 鎖相關(guān)基礎(chǔ)知識(shí)

zk 一般由多個(gè)節(jié)點(diǎn)構(gòu)成(單數(shù)),采用 zab 一致性協(xié)議。因此可以將 zk 看成一個(gè)單點(diǎn)結(jié)構(gòu),對(duì)其修改數(shù)據(jù)其內(nèi)部自動(dòng)將所有節(jié)點(diǎn)數(shù)據(jù)進(jìn)行修改而后才提供查詢(xún)服務(wù)。zk 的數(shù)據(jù)以目錄樹(shù)的形式,每個(gè)目錄稱(chēng)為 znode,znode 中可存儲(chǔ)數(shù)據(jù)(一般不超過(guò) 1M),還可以在其中增加子節(jié)點(diǎn)。QmS28資訊網(wǎng)——每日最新資訊28at.com

znode節(jié)點(diǎn)有三種類(lèi)型。序列化節(jié)點(diǎn),每在該節(jié)點(diǎn)下增加一個(gè)節(jié)點(diǎn)自動(dòng)給該節(jié)點(diǎn)的名稱(chēng)上添加序號(hào)并且自增1。臨時(shí)節(jié)點(diǎn),一旦創(chuàng)建這個(gè) znode 的客戶(hù)端與服務(wù)器失去聯(lián)系,這個(gè) znode 也將自動(dòng)刪除。最后就是普通節(jié)點(diǎn)。QmS28資訊網(wǎng)——每日最新資訊28at.com

Watch 機(jī)制,client 可以監(jiān)控每個(gè)節(jié)點(diǎn)的變化,當(dāng)產(chǎn)生變化時(shí) client 會(huì)接受到一個(gè)事件通知。QmS28資訊網(wǎng)——每日最新資訊28at.com

zk 基本鎖

原理:利用臨時(shí)節(jié)點(diǎn)與 watch 機(jī)制。每個(gè)鎖占用一個(gè)普通節(jié)點(diǎn) /lock,當(dāng)需要獲取鎖時(shí)在 /lock 目錄下創(chuàng)建一個(gè)臨時(shí)節(jié)點(diǎn),創(chuàng)建成功則表示獲取鎖成功,失敗則 watch/lock 節(jié)點(diǎn),有刪除操作后再去爭(zhēng)鎖。臨時(shí)節(jié)點(diǎn)好處在于當(dāng)進(jìn)程掛掉后能自動(dòng)上鎖的節(jié)點(diǎn)自動(dòng)刪除即取消鎖。QmS28資訊網(wǎng)——每日最新資訊28at.com

缺點(diǎn):所有取鎖失敗的進(jìn)程都監(jiān)聽(tīng)父節(jié)點(diǎn),很容易發(fā)生羊群效應(yīng),即當(dāng)釋放鎖后所有等待進(jìn)程一起來(lái)創(chuàng)建節(jié)點(diǎn),并發(fā)量很大,增加zk集群壓力。QmS28資訊網(wǎng)——每日最新資訊28at.com

zk 鎖優(yōu)化

原理:上鎖改為創(chuàng)建臨時(shí)有序節(jié)點(diǎn),每個(gè)上鎖的節(jié)點(diǎn)均能創(chuàng)建節(jié)點(diǎn)成功,只是其序號(hào)不同。只有序號(hào)最小的可以擁有鎖,如果這個(gè)節(jié)點(diǎn)序號(hào)不是最小的則 watch 序號(hào)比本身小的前一個(gè)節(jié)點(diǎn) (公平鎖)。QmS28資訊網(wǎng)——每日最新資訊28at.com

步驟:QmS28資訊網(wǎng)——每日最新資訊28at.com

?在 /lock 節(jié)點(diǎn)下創(chuàng)建一個(gè)有序臨時(shí)節(jié)點(diǎn) (EPHEMERAL_SEQUENTIAL)。QmS28資訊網(wǎng)——每日最新資訊28at.com

?判斷創(chuàng)建的節(jié)點(diǎn)序號(hào)是否最小,如果是最小則獲取鎖成功。不是則獲取鎖失敗,然后 watch 序號(hào)比本身小的前一個(gè)節(jié)點(diǎn)。QmS28資訊網(wǎng)——每日最新資訊28at.com

?當(dāng)取鎖失敗,設(shè)置 watch 后則等待 watch 事件到來(lái)后,再次判斷是否序號(hào)最小。QmS28資訊網(wǎng)——每日最新資訊28at.com

?取鎖成功則執(zhí)行代碼,最后釋放鎖(刪除該節(jié)點(diǎn))。QmS28資訊網(wǎng)——每日最新資訊28at.com

參考代碼:QmS28資訊網(wǎng)——每日最新資訊28at.com

@Slf4jpublic class DistributedLock implements Lock, Watcher{     /**      * zk客戶(hù)端      */      private ZooKeeper zk;     /**     * 根目錄     */     private final String root = "/locks";     /**     * 鎖名稱(chēng)     */     private final String lockName;     /**     * 等待前一個(gè)鎖     */     private String waitNode;     /**     * 當(dāng)前鎖     */     private String myZnode;     /**     * 計(jì)數(shù)器     */     private CountDownLatch latch;     /**     * 會(huì)話(huà)超時(shí)時(shí)間     */     private final int sessionTimeout = 30000;     /**     * 異常列表     */     private final List<Exception> exception = new ArrayList<>();     /**     * 創(chuàng)建分布式鎖     * @param config 服務(wù)器配置     * @param lockName 競(jìng)爭(zhēng)資源標(biāo)志,lockName中不能包含單詞lock     */     public DistributedLock(String config, String lockName){         this.lockName = lockName;         // 創(chuàng)建與服務(wù)器的連接         try {             zk = new ZooKeeper(config, sessionTimeout, this);             Stat stat = zk.exists(root, false);             if(stat == null){                 // 創(chuàng)建根節(jié)點(diǎn)                 zk.create(root, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);             }         } catch (IOException | KeeperException | InterruptedException e) {              exception.add(e);         }     }    /**     * zookeeper節(jié)點(diǎn)的監(jiān)視器     */     @Override     public void process(WatchedEvent event) {         if(this.latch != null) {             this.latch.countDown();         }     }     @Override     public void lock() {         if(!exception.isEmpty()){              throw new LockException(exception.get(0));         }         try {             if(this.tryLock()){                 log.info("Thread " + Thread.currentThread().getId() + " " +myZnode + " get lock true");             } else{                 //等待鎖                 waitForLock(waitNode, sessionTimeout);             }         } catch (KeeperException | InterruptedException e) {             throw new LockException(e);         }     }     @Override     public boolean tryLock() {         try {             String splitStr = "_lock_";             if(lockName.contains(splitStr)) {                 throw new LockException("lockName can not contains //u000B");         }         //創(chuàng)建臨時(shí)有序子節(jié)點(diǎn)         myZnode = zk.create(root + "/" + lockName + splitStr, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);         log.info(myZnode + " is created ");         //取出所有子節(jié)點(diǎn)         List<String> subNodes = zk.getChildren(root, false);         //取出所有l(wèi)ockName的鎖         List<String> lockObjNodes = new ArrayList<String>();         for (String node : subNodes) {             String _node = node.split(splitStr)[0];             if(_node.equals(lockName)){                 lockObjNodes.add(node);             }         }         Collections.sort(lockObjNodes);         log.info("myZnode={} minZnode={}", myZnode, lockObjNodes.get(0));         if(myZnode.equals(root+"/"+lockObjNodes.get(0))){             //如果是最小的節(jié)點(diǎn),則表示取得鎖             return true;         }         //如果不是最小的節(jié)點(diǎn),找到比自己小1的節(jié)點(diǎn)         String subMyZnode = myZnode.substring(myZnode.lastIndexOf("/") + 1);         waitNode = lockObjNodes.get(Collections.binarySearch(lockObjNodes, subMyZnode) - 1);         } catch (KeeperException | InterruptedException e) {             throw new LockException(e);         }         return false;     }     @Override     public boolean tryLock(long time,@NonNull TimeUnit unit) {         try {             if(this.tryLock()){                 return true;             }             return waitForLock(waitNode,time);         } catch (Exception e) {             log.error("tryLock exception:", e);         }         return false;     }     /**     * @param lower 監(jiān)視節(jié)點(diǎn)     * @param waitTime 等待超時(shí)時(shí)間     * @return 是否獲得鎖     * @throws InterruptedException     * @throws KeeperException     */      private boolean waitForLock(String lower, long waitTime) throws InterruptedException, KeeperException {          Stat stat = zk.exists(root + "/" + lower,true);          //判斷比自己小一個(gè)數(shù)的節(jié)點(diǎn)是否存在,如果不存在則無(wú)需等待鎖,同時(shí)注冊(cè)監(jiān)聽(tīng)          if(stat != null){             log.info("Thread " + Thread.currentThread().getId() + " waiting for " + root + "/" + lower);             this.latch = new CountDownLatch(1);             this.latch.await(waitTime, TimeUnit.MILLISECONDS);             this.latch = null;          }          return true;     }      /**      * 解鎖方法      * @throws InterruptedException 線(xiàn)程中斷異常      * @throws KeeperException ZooKeeper異常      */      @Override      public void unlock() {          try {              log.info("unlock " + myZnode);              zk.delete(myZnode,-1);              myZnode = null;              zk.close();         } catch (InterruptedException | KeeperException e) {             log.error("unlock exception:", e);         }     }     @Override     public void lockInterruptibly() throws InterruptedException {          this.lock();     }     @Override     public Condition newCondition() {         return null;     }     /**     * 自定義鎖異常     */     public static class LockException extends RuntimeException {         private static final long serialVersionUID = 1L;         /**         * @param e 異常         */         public LockException(String e){             super(e);         }         /**         * @param e 異常         */         public LockException(Exception e){             super(e);         }     }}

小結(jié)

優(yōu)點(diǎn):QmS28資訊網(wǎng)——每日最新資訊28at.com

?有效的解決單點(diǎn)問(wèn)題,不可重入問(wèn)題,非阻塞問(wèn)題以及鎖無(wú)法釋放的問(wèn)題。實(shí)現(xiàn)起來(lái)較為簡(jiǎn)單。QmS28資訊網(wǎng)——每日最新資訊28at.com

?具有良好的順序性和公平性,可以有效的避免死鎖和競(jìng)爭(zhēng)問(wèn)題。QmS28資訊網(wǎng)——每日最新資訊28at.com

?支持高可用,容錯(cuò)性較好,通過(guò)zookeeper集群可以確保鎖的可靠性和強(qiáng)一致性。QmS28資訊網(wǎng)——每日最新資訊28at.com

?有現(xiàn)成的第三方包和工具支持,實(shí)現(xiàn)起來(lái)相對(duì)簡(jiǎn)單。QmS28資訊網(wǎng)——每日最新資訊28at.com

不足:QmS28資訊網(wǎng)——每日最新資訊28at.com

?性能相對(duì)較低,ZK中創(chuàng)建和刪除節(jié)點(diǎn)只能通過(guò) Leader 服務(wù)器來(lái)執(zhí)行,然后將數(shù)據(jù)同步到所有的 Follower 機(jī)器上。QmS28資訊網(wǎng)——每日最新資訊28at.com

?需要維護(hù)ZooKeeper集群,增加了系統(tǒng)的復(fù)雜性和維護(hù)成本。QmS28資訊網(wǎng)——每日最新資訊28at.com

?在高并發(fā)場(chǎng)景下,頻繁的鎖操作可能導(dǎo)致ZooKeeper集群成為性能瓶頸。QmS28資訊網(wǎng)——每日最新資訊28at.com

分布式鎖的應(yīng)用

分布式鎖的應(yīng)用場(chǎng)景

分布式鎖在分布式系統(tǒng)中有著廣泛的應(yīng)用,主要體現(xiàn)在以下幾個(gè)方面:QmS28資訊網(wǎng)——每日最新資訊28at.com

1.共享資源競(jìng)爭(zhēng):當(dāng)多個(gè)進(jìn)程或線(xiàn)程嘗試同時(shí)訪(fǎng)問(wèn)或修改共享資源時(shí),為了避免數(shù)據(jù)沖突和不一致,可以使用分布式鎖來(lái)確保同一時(shí)刻只有一個(gè)節(jié)點(diǎn)可以訪(fǎng)問(wèn)資源。這在多機(jī)器或多節(jié)點(diǎn)的分布式系統(tǒng)中尤為重要,因?yàn)閭鹘y(tǒng)的單機(jī)并發(fā)控制策略可能不再適用。QmS28資訊網(wǎng)——每日最新資訊28at.com

2.效率性:使用分布式鎖可以避免不同節(jié)點(diǎn)或進(jìn)程重復(fù)執(zhí)行相同的任務(wù)或操作。例如,在任務(wù)調(diào)度系統(tǒng)中,如果多個(gè)節(jié)點(diǎn)都嘗試執(zhí)行同一任務(wù),通過(guò)使用分布式鎖,可以確保只有一個(gè)節(jié)點(diǎn)執(zhí)行該任務(wù),從而提高系統(tǒng)的整體效率。QmS28資訊網(wǎng)——每日最新資訊28at.com

3.特殊業(yè)務(wù)場(chǎng)景:在電商業(yè)務(wù)中,分布式鎖常用于處理高并發(fā)場(chǎng)景下的資源競(jìng)爭(zhēng)問(wèn)題。例如,在扣減庫(kù)存或防止流量過(guò)載時(shí),通過(guò)分布式鎖可以確保操作的原子性和一致性。此外,秒殺搶購(gòu)、優(yōu)惠券領(lǐng)取等場(chǎng)景也常利用分布式鎖來(lái)確保數(shù)據(jù)的一致性。QmS28資訊網(wǎng)——每日最新資訊28at.com

4.微服務(wù)架構(gòu):在微服務(wù)架構(gòu)的系統(tǒng)中,分布式鎖發(fā)揮著至關(guān)重要的作用。特別是在金融支付系統(tǒng)等對(duì)一致性要求極高的場(chǎng)景中,分布式鎖被廣泛應(yīng)用于實(shí)現(xiàn)各種特殊需求,確保操作的原子性、數(shù)據(jù)的準(zhǔn)確性和一致性。QmS28資訊網(wǎng)——每日最新資訊28at.com

總的來(lái)說(shuō),分布式鎖的主要應(yīng)用場(chǎng)景涉及需要確保數(shù)據(jù)一致性、防止數(shù)據(jù)沖突和提高系統(tǒng)效率的場(chǎng)景。通過(guò)使用分布式鎖,可以在分布式系統(tǒng)中實(shí)現(xiàn)更精細(xì)化的控制和協(xié)調(diào),確保系統(tǒng)的穩(wěn)定性和可靠性。QmS28資訊網(wǎng)——每日最新資訊28at.com

選型分析

根據(jù)以上實(shí)現(xiàn)原理的分析,選擇哪種分布式鎖方案取決于具體的應(yīng)用場(chǎng)景和需求。對(duì)于簡(jiǎn)單的應(yīng)用場(chǎng)景和對(duì)性能要求不高的系統(tǒng),基于MySQL的分布式鎖可能是一個(gè)不錯(cuò)的選擇。對(duì)于高并發(fā)、高性能要求的系統(tǒng),基于Redis的分布式鎖可能更合適。而如果需要確保鎖的公平性和一致性,并且對(duì)性能要求不是特別高,那么基于ZooKeeper的分布式鎖可能是一個(gè)更好的選擇。在實(shí)際應(yīng)用中,還需要根據(jù)系統(tǒng)的具體情況和需求進(jìn)行權(quán)衡和選擇。QmS28資訊網(wǎng)——每日最新資訊28at.com

關(guān)于布式鎖互斥性的進(jìn)一步討論

經(jīng)過(guò)以探討,我們可以得出一個(gè)結(jié)論:基于單機(jī)模式的MySQL、Redis以及ZooKeeper集群,均能夠嚴(yán)格實(shí)現(xiàn)分布式鎖,從而確保鎖的互斥性。這里之所以強(qiáng)調(diào)鎖的互斥性,是因?yàn)樗_保了同一時(shí)刻僅有一個(gè)進(jìn)程或線(xiàn)程能夠訪(fǎng)問(wèn)特定的共享資源,從而避免了數(shù)據(jù)沖突和不一致性的發(fā)生。QmS28資訊網(wǎng)——每日最新資訊28at.com

然而,當(dāng)我們轉(zhuǎn)向MySQL主從模式或Redis主從模式時(shí),情況便發(fā)生了變化。這些模式在保障鎖的互斥性方面存在明顯的不足。要深入探究這一現(xiàn)象的根源,我們不得不提及分布式領(lǐng)域中的一個(gè)關(guān)鍵理論——CAP理論。QmS28資訊網(wǎng)——每日最新資訊28at.com

從鎖的定義和特性出發(fā),我們知道,在獲取鎖的過(guò)程中,需要一個(gè)全局可見(jiàn)的標(biāo)識(shí)。當(dāng)一個(gè)進(jìn)程或線(xiàn)程成功獲取鎖后,該標(biāo)識(shí)會(huì)被設(shè)置并變得全局可見(jiàn),這樣其他線(xiàn)程就無(wú)法突破鎖的互斥性限制,確保鎖的互斥性得到維護(hù)。而這一切的前提,便是數(shù)據(jù)必須保持一致性。QmS28資訊網(wǎng)——每日最新資訊28at.com

然而,主從模式更傾向于保障可用性和分區(qū)容忍性,即AP模型,這在一定程度上犧牲了數(shù)據(jù)的一致性。相比之下,ZooKeeper集群則采用了CP模型,即保證一致性和分區(qū)容忍性。因此,在分布式環(huán)境下,ZooKeeper集群能夠確保數(shù)據(jù)的一致性,從而確保鎖的互斥性得到嚴(yán)格保障。QmS28資訊網(wǎng)——每日最新資訊28at.com

綜上所述,在分布式系統(tǒng)中,確保鎖的互斥性至關(guān)重要。我們?cè)谶x擇和設(shè)計(jì)分布式鎖時(shí),必須充分考慮其互斥性保障能力,并結(jié)合實(shí)際場(chǎng)景和需求,選擇最合適的實(shí)現(xiàn)方案。當(dāng)業(yè)務(wù)場(chǎng)景需要高可靠性的分布式鎖時(shí),ZooKeeper集群因其出色的數(shù)據(jù)一致性保障能力,自然成為了一個(gè)更加值得考慮的優(yōu)秀選擇。QmS28資訊網(wǎng)——每日最新資訊28at.com

分布式鎖的挑戰(zhàn)

雖然分布式鎖為分布式系統(tǒng)帶來(lái)了諸多好處,但在實(shí)際應(yīng)用中也面臨一些挑戰(zhàn):QmS28資訊網(wǎng)——每日最新資訊28at.com

1.性能問(wèn)題:分布式鎖的獲取和釋放需要通過(guò)網(wǎng)絡(luò)通信,這可能會(huì)引入額外的性能開(kāi)銷(xiāo)。在高并發(fā)場(chǎng)景下,如果大量進(jìn)程或線(xiàn)程爭(zhēng)用同一個(gè)鎖,可能導(dǎo)致性能瓶頸。QmS28資訊網(wǎng)——每日最新資訊28at.com

2.可靠性問(wèn)題:分布式鎖的可靠性受到網(wǎng)絡(luò)、硬件、軟件等多方面因素的影響。如果鎖服務(wù)出現(xiàn)故障或網(wǎng)絡(luò)中斷,可能導(dǎo)致死鎖或數(shù)據(jù)不一致等問(wèn)題。QmS28資訊網(wǎng)——每日最新資訊28at.com

3.可擴(kuò)展性問(wèn)題:隨著分布式系統(tǒng)的規(guī)模不斷擴(kuò)大,如何確保分布式鎖的可擴(kuò)展性成為一個(gè)重要問(wèn)題。需要設(shè)計(jì)合理的分布式鎖策略,以適應(yīng)不同規(guī)模和需求的系統(tǒng)。QmS28資訊網(wǎng)——每日最新資訊28at.com

本文主要討論了分布式鎖的原理和不同的實(shí)現(xiàn)方案,有基于數(shù)據(jù)庫(kù),Redis和ZooKeeper三種選擇,并且各有優(yōu)缺點(diǎn)。項(xiàng)目開(kāi)發(fā)過(guò)程中根據(jù)自己實(shí)際的業(yè)務(wù)場(chǎng)景,選擇適合自己項(xiàng)目的方案。QmS28資訊網(wǎng)——每日最新資訊28at.com

QmS28資訊網(wǎng)——每日最新資訊28at.com

文章中難免會(huì)有不足之處,希望讀者能給予寶貴的意見(jiàn)和建議。謝謝!QmS28資訊網(wǎng)——每日最新資訊28at.com

參考文檔

https://cloud.tencent.com/developer/article/1909596QmS28資訊網(wǎng)——每日最新資訊28at.com

https://zhuanlan.zhihu.com/p/42056183QmS28資訊網(wǎng)——每日最新資訊28at.com

本文鏈接:http://www.tebozhan.com/showinfo-26-87988-0.html深入理解分布式鎖:原理、應(yīng)用與挑戰(zhàn)

聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com

上一篇: 一篇文章徹底搞懂Arthas的原理,你學(xué)會(huì)了嗎?

下一篇: 一篇學(xué)會(huì)Go中reflect反射的詳細(xì)用法

標(biāo)簽:
  • 熱門(mén)焦點(diǎn)
Top