日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

分布式系统概念 | 分布式锁:数据库、Redis、Zookeeper解决方案

發布時間:2024/4/11 windows 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 分布式系统概念 | 分布式锁:数据库、Redis、Zookeeper解决方案 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 分布式鎖
    • 數據庫
      • 唯一索引
    • Redis
      • SETNX、EXPIRE
      • RedLock算法
    • Zookeeper
      • 實現原理
      • 羊群效應
      • 改進方法
    • 總結


分布式鎖

隨著互聯網技術的不斷發展、數據量的大幅增加、業務邏輯的復雜化導致傳統的集中式系統已經無法應用于當前的業務場景,因此分布式系統被應用在越來越多的地方,但是在分布式系統中,由于網絡、機器(如網絡延遲、分區,機器宕機)等情況導致場景更加復雜,充滿了不可靠的情況。為了保證一致性,在這種情況下我們就需要用到分布式鎖

那么分布式鎖需要具備哪些條件呢?

  • 獲取、釋放鎖的性能要高
  • 判斷鎖的獲取操作必須要是原子的(防止同一個鎖被多個節點獲取)
  • 網絡或者機器出現問題導致無法繼續工作時,必須要釋放鎖(防止死鎖)
  • 可重入的,一個線程可以多次獲取同一把鎖(防止死鎖)
  • 阻塞鎖(依據業務需求)

但是目前并沒有能夠滿足上面所有要求的完美結局方案,對于分布式鎖,我們通常使用以下三種方法來實現

  • 數據庫
  • Redis(緩存)
  • Zookeeper
  • 數據庫

    唯一索引

    我們可以利用數據庫中的唯一索引來實現。由于唯一索引能夠保證記錄只被插入一次,因此我們可以利用其判斷當前是否處于鎖定狀態。所以當想要獲取鎖的時候,就向數據庫中插入一條記錄,而釋放鎖的時候就刪除這條記錄即可。

    但是該方法存在以下問題

    • 鎖沒有失效時間,如果解鎖失敗的話其他進程無法再獲得該鎖(死鎖)
    • 非阻塞鎖,插入失敗就直接報錯,沒有辦法進入隊列重試
    • 不可重入,同一線程在沒有釋放鎖之前無法重復獲得該鎖

    對于數據庫來說我們還可以選擇使用排他鎖、樂觀鎖等方法來實現分布式鎖,但是由于這些方法對原表有侵入、占用數據庫連接等情況,一般情況下都不做考慮,因此這里也就不詳細描述。


    Redis

    SETNX、EXPIRE

    我們可以利用setnx(set if not exist)命令來實現鎖。只有在緩存中不存在Key的時候才會set并返回true,而Key已存在的時候就直接返回false。同時為了防止獲取鎖失敗而導致的死鎖情況,我們可以利用expire命令對這個key設置一個超時時間。

    為了防止我們setnx成功之后線程發生異常中斷導致我們來不及設置expire而導致死鎖,我們通常會使用以下命令來設置

    SET key random_value NX EX 30000 /*EX seconds – 設置鍵key的過期時間,單位時秒PX milliseconds – 設置鍵key的過期時間,單位時毫秒NX – 只有鍵key不存在的時候才會設置key的值XX – 只有鍵key存在的時候才會設置key的值 */

    該命令僅在Key不存在(NX選項)時才插入,并且設置到期時間為30000毫秒(PX選項),value設置為隨機值,該值在所有客戶端和所有鎖定請求中必須唯一(防止被他人誤刪)。

    當我們想要釋放鎖時,為了保證安全(防止誤刪除另一個客戶端創建的鎖),僅當密鑰存在且存儲在密鑰上的值恰好是期望的值時,才刪除該密鑰,下面是以lua腳本完成的刪除邏輯

    if redis.call("get",KEYS[1]) == ARGV[1] thenreturn redis.call("del",KEYS[1]) elsereturn 0 end

    這種方法雖然實現起來非常簡單,但是其存在著單點問題,它加鎖時只作用于一個Redis節點上,如果該節點出現故障故障,即使使用哨兵來保證高可用,也會出現鎖丟失的情況,如下場景

  • 在Redis的Master節點拿到鎖,此時鎖還沒有同步到Slave節點
  • 此時Master發生故障,哨兵主導進行故障轉移,Slave節點升級為Master節點
  • 由于鎖沒來得及同步,因此導致鎖丟失
  • 考慮到這種情況,Redis作者antirez基于分布式環境下提出了一種更高級的分布式鎖的實現方式:Redlock

    RedLock算法

    Redlock 是 Redis 的作者 antirez 給出的集群模式的 Redis 分布式鎖,它基于 N 個完全獨立不存在主從復制或者其他集群協調機制)的 Redis節點(通常情況下 N 設置成 5,為了資源的合理利用通常為奇數)。

    算法的流程如下

  • 獲取當前時間
  • 客戶端依次嘗試從5個(奇數)相互獨立的Redis實例中使用相同的key和具有唯一性的value獲取鎖
  • 計算獲取鎖消耗的時間,只有時間小于鎖的過期時間,并且從**大多數(N / 2 + 1)**實例上獲取了鎖,才認為獲取鎖成功;
  • 如果獲取了鎖,則重新計算有效期時間,即原有效時間減去獲取鎖消耗的時間
  • 如果客戶端獲取鎖失敗,則釋放所有實例上的鎖
  • 雖然RedLock算法比上面的單點Redis鎖更可靠,但是由于分布式的復雜性,實現起來的條件也更加的苛刻。

  • 由于必須獲取 (N / 2 + 1) 個節點上的鎖,所以可能會出現鎖沖突的情況(即每個人都獲取了一些鎖,但是沒有人獲取一半以上的鎖)。針對這個問題,其借鑒了Raft算法的思路,即產生沖突后為每個節點設置一個隨機開始時間,在時間到后重新嘗試獲取鎖,但是這也導致了獲取鎖的成本增加。
  • 如果5個節點有2個宕機,鎖的可用性會極大降低,因為必須等待這兩個宕機節點的結果超時才能返回。另外只剩3個節點,客戶端必須獲取到這全部3個節點的鎖才能擁有鎖,加鎖難度也加大了。
  • 如果出現網絡分區,那么可能出現客戶端永遠也無法獲取鎖的情況。
  • 介于以上情況,我們還可以選擇更加可靠的方法,即Zookeeper實現的分布式鎖。


    Zookeeper

    Zookeeper是一個為分布式應用提供一致性服務的軟件,它內部是一個分層的文件系統目錄樹結構,規定同一個目錄下只能有一個唯一文件名。

    由于Zookeeper同樣沒有實現鎖API,所以我們利用其數據節點來表示鎖,數據節點分為以下三種類型

    • 永久節點:節點創建后永久存在,不會因為會話的消失而消失
    • 臨時節點:與永久節點相反,當客戶端結束會話后立即刪除
    • 順序節點:會在節點名的后面加一個數字后綴,并且是有序的,例如生成的有序節點為 /lock/node-0000000000,它的下一個有序節點則為 /lock/node-0000000001,以此類推。

    實現原理

    除了上面介紹的節點外,我們還需要用到Watcher(監視器)來注冊對節點狀態的監聽

    • Watcher:注冊一個該節點的監視器,當節點狀態發生變化時,Watcher就會觸發,此時Zookeeper將會向客戶端發送一條通知(Watcher只能被觸發一次)

    根據上述特性,我們就可以使用臨時順序節點與Watcher來實現分布式鎖

  • 創建一個鎖目錄/lock
  • 當需要獲取鎖時,就在lock目錄下創建臨時順序節點
  • 獲取/lock下的子節點列表,判斷自己創建的子節點是否為當前子節點列表中序號最小的子節點,如果是則認為獲得鎖;否則到lock目錄下注冊一個子節點變更的Watcher監聽,獲得子節點的變更通知后重復此步驟直至獲得鎖
  • 執行完業務邏輯后,主動刪除自己的節點來釋放鎖。此時會觸發其他節點的Watcher,讓其他人獲取鎖。
  • 那如果出現網絡中斷或者機器宕機,鎖還能釋放嗎?

    這里需要注意的是,我們使用的是臨時節點,所以當客戶端因為某種原因無法繼續工作時,就會導致會話的中斷,臨時節點就會被Zookeeprer自動刪除。這也就是Zookeeper相較于Redis更加可靠的原因。

    羊群效應

    上面這個實現方法,大體上能夠滿足一般的分布式集群競爭鎖的需求,并且能夠保證一定的性能。但是隨著機器規模的擴大,其效率會越來越低。

    為什么呢?我們思考一下鎖的釋放流程

    在我們獲取鎖失敗后,會注冊一個對lock目錄的Watcher監控,當有節點變更消息時,就會通知給所有注冊了的機器。然而這個通知除了使序號最小的節點獲取鎖外,對其他的節點沒有產生任何實際作用。

    性能瓶頸的原因就是上面這個問題,大量的Watcher通知子節點列表獲取兩個操作重復運行,并且絕大多數運行結果都是判斷出自己并非是序號最小的節點,從而繼續等待下一次的通知,浪費了大量的資源。

    如果集群規模較大,不僅會對Zookeeper服務器造成巨大的性能影響和網絡沖擊,更嚴重的時候甚至會因為多個節點對應的客戶端同時釋放鎖導致大量的節點消失,從而短時間內向剩余客戶端發送大量的事件通知——這就是所謂的羊群效應

    改進方法

    羊群效應出現根源在于其沒有找到事件的真正關注點,對于分布式鎖的競爭過程來說,它的核心邏輯就是判斷自己是否是所有子節點中序號最小的。那么問題就簡單了,我們只需要關注比自己序號小的那一個相關節點的變更情況就可以了,而不再需要關注全局的子列表變更情況。

    于是,改進后的獲取流程如下

  • 創建一個鎖目錄/lock
  • 當需要獲取鎖時,就在lock目錄下創建臨時順序節點
  • 獲取/lock下的子節點列表,判斷自己創建的子節點是否為當前子節點列表中序號最小的子節點,如果是則認為獲得鎖;否則到Watcher自己的次小節點(防止羊群效應)
  • 執行完業務邏輯后,主動刪除自己的節點來釋放鎖,此時會觸發順序的下一個節點的Watcher
  • 總結

    方案優點缺點應用場景
    數據庫直接使用數據庫,操作簡單分布式系統的性能瓶頸大部分都在數據庫,而使用數據庫鎖加大了負擔業務邏輯簡單,對性能要求不高
    Redis性能較高,且實現起來方便鎖超時機制不可靠,當線程獲取鎖時,可能因為處理時間過長導致鎖超時失效追求高性能,允許偶發的鎖失效問題
    ZooKeeper不依賴超時時間釋放鎖,可靠性高由于頻繁的創建和刪除節點,性能比不上Redis鎖系統要求高可靠性

    總結

    以上是生活随笔為你收集整理的分布式系统概念 | 分布式锁:数据库、Redis、Zookeeper解决方案的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。