分布式鎖解決方案

2021-10-16 22:02:28 字數 3618 閱讀 8663

分布式鎖三種實現方式:

1. 基於資料庫實現分布式鎖;

2. 基於快取(redis等)實現分布式鎖;

3. 基於zookeeper實現分布式鎖;

1. 悲觀鎖

利用select … where … for update 排他鎖

注意: 其他附加功能與實現一基本一致,這裡需要注意的是「where name=lock 」,name欄位必須要走索引,否則會鎖表。有些情況下,比如表不大,mysql優化器會不走這個索引,導致鎖表問題。

2. 樂觀鎖

所謂樂觀鎖與前邊最大區別在於基於cas思想,是不具有互斥性,不會產生鎖等待而消耗資源,操作過程中認為不存在併發衝突,只有update version失敗後才能覺察到。我們的搶購、秒殺就是用了這種實現以防止超賣。

通過增加遞增的版本號字段實現樂觀鎖

1. 使用命令介紹:

(1)setnx

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

(2)expire

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

(3)delete

delete key:刪除key

在使用redis實現分布式鎖的時候,主要就會使用到這三個命令。

2. 實現思想:

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

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

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

3. 分布式鎖的簡單實現**:

/**  

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

*/ 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; 

}}

4. 測試剛才實現的分布式鎖

例子中使用50個執行緒模擬秒殺乙個商品,使用–運算子來實現商品減少,從結果有序性就可以看出是否為加鎖狀態。

模擬秒殺服務,在其中配置了jedis執行緒池,在初始化的時候傳給分布式鎖,供其使用。

public class service   

public void seckill()

}

模擬線程進行秒殺服務:

public class threada extends thread  

@override public void run()

}public class test

}}

結果如下,結果為有序的:

若注釋掉使用鎖的部分:

public void seckill()
從結果可以看出,有一些是非同步進行的:

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

(1)建立乙個目錄mylock;

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

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

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

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

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

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

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

資料庫分布式鎖實現

缺點:1.db操作效能較差,並且有鎖表的風險

2.非阻塞操作失敗後,需要輪詢,占用cpu資源;

3.長時間不commit或者長時間輪詢,可能會占用較多連線資源

redis(快取)分布式鎖實現

缺點:1.鎖刪除失敗 過期時間不好控制

2.非阻塞,操作失敗後,需要輪詢,占用cpu資源;

zk分布式鎖實現

缺點:效能不如redis實現,主要原因是寫操作(獲取鎖釋放鎖)都需要在leader上執行,然後同步到follower。

總之:zookeeper有較好的效能和可靠性。

從理解的難易程度角度(從低到高)資料庫 > 快取 > zookeeper

從實現的複雜性角度(從低到高)zookeeper >= 快取 > 資料庫

從效能角度(從高到低)快取 > zookeeper >= 資料庫

從可靠性角度(從高到低)zookeeper > 快取 > 資料庫

zk分布式鎖解決方案

思路 加鎖時,在指定路徑建立臨時節點 臨時節點避免死鎖 於是只有乙個執行緒能建立成功 等待時,其他建立節點失敗的執行緒,會watch指定路徑的刪除事件,一旦事件觸發,說明臨時節點被刪除,執行緒可以繼續去獲取鎖 解鎖時,當前執行緒刪除它建立的節點 public class zookeeperdistr...

分布式環境下的解決方案 分布式鎖

分布式鎖,也就是在多程序情況下的鎖。需要有儲存鎖的空間,並且鎖的空間是可以訪問到的。鎖需要被唯一標識。鎖要有至少兩種狀態。儲存空間 鎖是乙個抽象的概念,鎖的實現,需要依存於乙個可以儲存鎖的空間。在多執行緒中是記憶體,在多程序中是記憶體或者磁碟。更重要的是,這個空間是可以被訪問到的。多執行緒中,不同的...

分布式鎖解決方案 Redis和Zookeeper

採用資料庫 不建議 效能不好 jdbc 基於redis實現分布式鎖 setnx setnx也可以存入key,如果存入key成功返回1,如果存入的key已經存在了,返回0.基於zookeeper實現分布式鎖 zookeeper是乙個分布式協調工具,在分布式解決方案中。多個客戶端 jvm 同時在zk上建...