目錄
為什麼需要分散式鎖定
實作方式
首頁 資料庫 Redis Redis中為什麼需要分散式鎖?如何實現?

Redis中為什麼需要分散式鎖?如何實現?

Oct 20, 2021 am 10:37 AM
redis 分散式鎖

這篇文章跟大家介紹一下Redis中的分散式鎖,介紹一下為什麼需要分散式鎖,Redis是如何實現分散式鎖的,希望對大家有幫助!

Redis中為什麼需要分散式鎖?如何實現?

為什麼需要分散式鎖定

#為什麼需要分散式鎖定

使用分散式鎖的目的,無外乎就是保證同一時間只有一個客戶端可以對共享資源進行操作。

我們在分散式應用程式進行邏輯處理時經常會遇到並發問題。 【相關推薦:Redis影片教學

例如一個操作要修改使用者的狀態,修改狀態需要先讀出使用者的狀態,在記憶體裡進行修改,改完了再存回去。如果這樣的操作同時進行了,就會出現並發問題,因為讀取和保存狀態這兩個操作不是原子的。

這個時候就要使用到分散式鎖定來限製程式的並發執行。 redis作為快取中間件系統,就能提供這個分散式鎖定機制,

其本質就是在redis裡面佔一個坑,當別的進程也要來佔坑時,發現已經被佔領了,就只要等待稍後再嘗試

#一般來說,生產環境可用的分散式鎖定需要滿足以下幾點:

  • 互斥性,互斥是鎖的基本特徵,同一時刻只能有一個執行緒持有鎖,執行臨界操作;
  • 逾時釋放,超時釋放是鎖的另一個必備特性,可以比較MySQL InnoDB 引擎中的innodb_lock_wait_timeout配置,透過逾時釋放,防止不必要的執行緒等待和資源浪費;
  • 可重入性,在分散式環境下,在同一個節點上的同一個執行緒如果取得了鎖定之後,再次請求還是可以成功;

實作方式

##使用SETNX實作

SETNX的使用方式為:

SETNX key value,只在鍵key不存在的情況下,將鍵key的值設為value,若鍵key存在,則SETNX不做任何動作。

boolean result = jedis.setnx("lock-key",true)== 1L;
if  (result) {
    try {
        // do something
    } finally {
        jedis.del("lock-key");
    }
 }
登入後複製

這種方案有一個致命問題,就是某個線程在獲取鎖之後由於某些異常因素(比如宕機)而不能正常的執行解鎖操作,那麼這個鎖就永遠釋放不掉了。

為此,我們可以為這個鎖定加上一個逾時時間

執行

SET key value EX seconds 的效果等同於執行SETEX key seconds value

執行

SET key value PX milliseconds 的效果等於執行PSETEX key milliseconds value##<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>String result = jedis.set(&quot;lock-key&quot;,true, 5); if (&quot;OK&quot;.equals(result)) { try { // do something } finally { jedis.del(&quot;lock-key&quot;); } }</pre><div class="contentsignin">登入後複製</div></div>

方案看起來很完美,但實際上還是會有問題

試想一下,某線程A獲取了鎖並且設定了過期時間為10s,然後在執行業務邏輯的時候耗費了15s,此時線程A獲取的鎖早已被Redis的過期機制自動釋放了

在線程A獲取鎖並經過10s之後,改鎖可能已經被其它線程獲取到了。當執行緒A執行完業務邏輯準備解鎖(

DEL key

)的時候,有可能刪除掉的是其它執行緒已經取得到的鎖定。 所以最好的方式是在解鎖時判斷鎖是否是自己的,我們可以在設定

key

#的時候將value設定為一個唯一值uniqueValue(可以是隨機值、UUID、或機器號線程號的組合、簽名等)。 當解鎖時,也就是刪除key的時候先判斷一下key對應的value是否等於先前設定的值,如果相等才能刪除key

String velue= String.valueOf(System.currentTimeMillis())
String result = jedis.set("lock-key",velue, 5);
if ("OK".equals(result)) {
    try {
        // do something
    } finally {
      	//非原子操作
	      if(jedis.get("lock-key")==value){
		        jedis.del("lock-key");
        }    
    }
}
登入後複製

這裡我們一眼就可以看出問題來:

GET

DEL是兩個分開的操作,在GET執行之後且在DEL執行之前的間隙是可能會發生異常的。 如果我們只要保證解鎖的程式碼是原子性的就能解決問題了

這裡我們引入了一種新的方式,就是

Lua腳本

,範例如下:

if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end
登入後複製
其中

ARGV[1]

表示設定key時指定的唯一值。 由於Lua腳本的原子性,在Redis執行該腳本的過程中,其他客戶端的命令都需要等待該Lua腳本執行完才能執行。

確保過期時間大於業務執行時間

為了防止多個執行緒同時執行業務程式碼,需要確保過期時間大於業務執行時間

#增加一個boolean類型的屬性

isOpenExpirationRenewal

,用來標識是否開啟定時刷新過期時間在增加一個

scheduleExpirationRenewal

#方法用於開啟刷新過期時間的執行緒 加鎖代碼在取得鎖定成功後將isOpenExpirationRenewal置為true,並且呼叫

scheduleExpirationRenewal

方法,開啟刷新過期時間的執行緒解鎖碼增加一行程式碼,將isOpenExpirationRenewal屬性置為false,停止刷新過期時間的線程輪詢

Redisson實現

獲取鎖定成功就會開啟一個定時任務,定時任務會定期檢查去續期

此定時調度每次呼叫的時間差是internalLockLeaseTime / 3,也就10秒

預設情況下,加鎖的時間是30秒.如果加鎖的業務沒有執行完,那麼到30-10 = 20秒的時候,就會進行一次續期,把鎖定重置成30秒

##RedLock

#在叢集中,主節點掛掉時,從節點會取而代之,客戶端上卻並沒有明顯感知。原先第一個客戶端在主節點中申請成功了一把鎖,但這把鎖還沒來得及同步到從節點,主節點突然掛掉了。然後從節點變成了主節點,這個新的節點內部沒有這個鎖,所以當另一個客戶端過來請求加鎖時,立即就批准了。這樣就會導致系統中同樣一把鎖被兩個客戶端同時持有,不安全性由此產生

Redlock演算法就是為了解決這個問題

使用Redlock,需要提供多個

Redis 實例,這些實例之前相互獨立沒有主從關係。同許多分散式演算法一樣,redlock 也使用大多數機制

加鎖時,它會向過半節點發送set指令,只要過半節點

set 成功,那就認為加鎖成功。釋放鎖定時,需要向所有節點發送 del 指令。不過Redlock 演算法還需要考慮出錯重試、時脈漂移等許多細節問題,同時因為Redlock 需要向多個節點進行讀寫,這意味著相較於單一實例Redis 效能會下降一些

Redlock 演算法是在單Redis 節點基礎上引入的高可用模式,Redlock 基於N 個完全獨立的Redis 節點,一般是大於3 的奇數個(通常情況下N 可以設定為5),可以基本保證叢集內各節點不會同時宕機。

假設目前叢集有5 個節點,執行Redlock 演算法的客戶端依序執行下面各個步驟,來完成取得鎖定的操作

    客戶端記錄當前系統時間,以毫秒為單位;
  • 依序嘗試從5 個Redis 實例中,使用相同的key 取得鎖,當向Redis 請求取得鎖定時,客戶端應該設定一個網路連線和回應逾時時間,超時時間應該小於鎖的失效時間,避免因為網路故障出現的問題;
  • 客戶端使用當前時間減去開始獲取鎖定時間就得到了獲取鎖使用的時間,當且僅當從半數以上的Redis 節點取得到鎖,並且當使用的時間小於鎖失效時間時,鎖才算獲取成功;
  • 如果獲取到了鎖,key 的真正有效時間等於有效時間減去獲取鎖所使用的時間,減少超時的幾率;
  • 如果獲取鎖定失敗,客戶端應該在​​所有的Redis 實例上進行解鎖,即使是上一步操作請求失敗的節點,防止因為服務端響應訊息丟失,但是實際數據添加成功導致的不一致。
也就是說,假設鎖定30秒過期,三個節點加鎖花了31秒,自然是加鎖失敗了

在Redis 官方推薦的Java 用戶端

Redisson 中,內建了對RedLock 的實作

#https://redis.io/topics/distlock

https:// github.com/redisson/redisson/wiki

RedLock問題:

RedLock 只是保證了鎖的高可用性,並沒有保證鎖的正確性

RedLock 是一個

嚴重依賴系統時脈的分散式系統

Martin 對RedLock 的批評:

    #對於提升效率的場景下, RedLock 太重。
  • 對於對正確性要求極高的場景下,RedLock 並不能保證正確性。
本文轉載自:https://juejin.cn/post/7018968452136173576

作者:心懷遠方

更多程式相關知識,請訪問:

編程視頻! !

以上是Redis中為什麼需要分散式鎖?如何實現?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

<🎜>:泡泡膠模擬器無窮大 - 如何獲取和使用皇家鑰匙
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系統,解釋
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆樹的耳語 - 如何解鎖抓鉤
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1666
14
CakePHP 教程
1426
52
Laravel 教程
1328
25
PHP教程
1273
29
C# 教程
1253
24
redis集群模式怎麼搭建 redis集群模式怎麼搭建 Apr 10, 2025 pm 10:15 PM

Redis集群模式通過分片將Redis實例部署到多個服務器,提高可擴展性和可用性。搭建步驟如下:創建奇數個Redis實例,端口不同;創建3個sentinel實例,監控Redis實例並進行故障轉移;配置sentinel配置文件,添加監控Redis實例信息和故障轉移設置;配置Redis實例配置文件,啟用集群模式並指定集群信息文件路徑;創建nodes.conf文件,包含各Redis實例的信息;啟動集群,執行create命令創建集群並指定副本數量;登錄集群執行CLUSTER INFO命令驗證集群狀態;使

redis數據怎麼清空 redis數據怎麼清空 Apr 10, 2025 pm 10:06 PM

如何清空 Redis 數據:使用 FLUSHALL 命令清除所有鍵值。使用 FLUSHDB 命令清除當前選定數據庫的鍵值。使用 SELECT 切換數據庫,再使用 FLUSHDB 清除多個數據庫。使用 DEL 命令刪除特定鍵。使用 redis-cli 工具清空數據。

redis怎麼讀取隊列 redis怎麼讀取隊列 Apr 10, 2025 pm 10:12 PM

要從 Redis 讀取隊列,需要獲取隊列名稱、使用 LPOP 命令讀取元素,並處理空隊列。具體步驟如下:獲取隊列名稱:以 "queue:" 前綴命名,如 "queue:my-queue"。使用 LPOP 命令:從隊列頭部彈出元素並返回其值,如 LPOP queue:my-queue。處理空隊列:如果隊列為空,LPOP 返回 nil,可先檢查隊列是否存在再讀取元素。

centos redis如何配置Lua腳本執行時間 centos redis如何配置Lua腳本執行時間 Apr 14, 2025 pm 02:12 PM

在CentOS系統上,您可以通過修改Redis配置文件或使用Redis命令來限制Lua腳本的執行時間,從而防止惡意腳本佔用過多資源。方法一:修改Redis配置文件定位Redis配置文件:Redis配置文件通常位於/etc/redis/redis.conf。編輯配置文件:使用文本編輯器(例如vi或nano)打開配置文件:sudovi/etc/redis/redis.conf設置Lua腳本執行時間限制:在配置文件中添加或修改以下行,設置Lua腳本的最大執行時間(單位:毫秒)

redis命令行怎麼用 redis命令行怎麼用 Apr 10, 2025 pm 10:18 PM

使用 Redis 命令行工具 (redis-cli) 可通過以下步驟管理和操作 Redis:連接到服務器,指定地址和端口。使用命令名稱和參數向服務器發送命令。使用 HELP 命令查看特定命令的幫助信息。使用 QUIT 命令退出命令行工具。

redis計數器怎麼實現 redis計數器怎麼實現 Apr 10, 2025 pm 10:21 PM

Redis計數器是一種使用Redis鍵值對存儲來實現計數操作的機制,包含以下步驟:創建計數器鍵、增加計數、減少計數、重置計數和獲取計數。 Redis計數器的優勢包括速度快、高並發、持久性和簡單易用。它可用於用戶訪問計數、實時指標跟踪、遊戲分數和排名以及訂單處理計數等場景。

redis過期策略怎麼設置 redis過期策略怎麼設置 Apr 10, 2025 pm 10:03 PM

Redis數據過期策略有兩種:定期刪除:定期掃描刪除過期鍵,可通過 expired-time-cap-remove-count、expired-time-cap-remove-delay 參數設置。惰性刪除:僅在讀取或寫入鍵時檢查刪除過期鍵,可通過 lazyfree-lazy-eviction、lazyfree-lazy-expire、lazyfree-lazy-user-del 參數設置。

如何優化debian readdir的性能 如何優化debian readdir的性能 Apr 13, 2025 am 08:48 AM

在Debian系統中,readdir系統調用用於讀取目錄內容。如果其性能表現不佳,可嘗試以下優化策略:精簡目錄文件數量:盡可能將大型目錄拆分成多個小型目錄,降低每次readdir調用處理的項目數量。啟用目錄內容緩存:構建緩存機制,定期或在目錄內容變更時更新緩存,減少對readdir的頻繁調用。內存緩存(如Memcached或Redis)或本地緩存(如文件或數據庫)均可考慮。採用高效數據結構:如果自行實現目錄遍歷,選擇更高效的數據結構(例如哈希表而非線性搜索)存儲和訪問目錄信

See all articles