分佈式鎖 - redis鎖、zookeeper鎖

應用場景:

分佈式鎖是爲了保證同一時刻只有一臺機器的一個線程執行某段代碼。分佈式鎖的目的如下

  • 解決業務層冪等性

  • 解決 MQ 消費端多次接受同一消息

  • 確保串行|隔離級別

  • 多臺機器同時執行定時任務

最近在工作中遇到了一些問題,上游重複調用下游接口下發數據導致數據重複,需要用redis鎖防重,線程獲取不到鎖時直接提示給上游已經下發過數據。

redis分佈式鎖:

主要實現是調用redis的SETNX命令(set if not exists),如果返回1,則獲得資源並加鎖;如果返回0,則沒有獲得鎖,鎖被其他資源佔用。需要注意的是:

  1. 必須設置過期時間,防止獲得鎖的服務器宕機後產生死鎖
  2. 添加redis數據必須與設置過期時間在同一個命令中,不能分開寫,因爲redis只保證單條命令的原子性,防止set完數據後還沒設置過期時間就宕機產生死鎖
  3. value應該是客戶端生成的唯一的字符串,解鎖時進行判斷,防止釋放了別人的鎖。(例:客戶端1在執行釋放鎖之前,鎖已經過期自動刪除,此時客戶端2拿到了鎖,客戶端1執行del操作就會釋放客戶端2的鎖)
  4. 釋放鎖的操作必須使用Lua腳本來實現。釋放鎖其實包含三步操作:GET、判斷和DEL,用Lua腳本來實現能保證這三步的原子性
  • 獲得鎖:

SET lock_key random_value NX PX 5000 (SET key value [NX|XX] [EX|PX] seconds  )
  1.         NX – 只有鍵key不存在的時候纔會設置key的值
  2.         XX – 只有鍵key存在的時候纔會設置key的值
  3.         EX seconds – 設置鍵key的過期時間,單位時秒
  4.         PX milliseconds – 設置鍵key的過期時間,單位時毫秒
  • 釋放鎖:

if redis.call('get',KEYS[1]) == ARGV[1] then 
   return redis.call('del',KEYS[1]) 
else
   return 0 
end

redis分佈式鎖存在一些問題:1、鎖時間的設置困難,太短可能過早的釋放鎖,造成數據安全問題。太長的話,如果客戶端掛掉會長時間無法釋放鎖,導致其他客戶端鎖請求阻塞或者失敗,因此需要程序員有豐富的經驗;2、爲了保證redis的高可用必然要搭建集羣,但redis主從同步會有時間間隔,如果一個客戶端已經從主節點獲得了鎖,一旦主節點掛掉或者網絡抖動導致切換到從節點後,就可能有另一個客戶端重複獲得鎖。那爲什麼還廣泛使用redis分佈式鎖呢?我們常用 N 個9 來量化可用性,只要能保證高可用性效果就達到了。

參考文章:高可用的分佈式鎖如何設計

發佈了68 篇原創文章 · 獲贊 47 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章