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("lock-key",true, 5);
if ("OK".equals(result)) {
try {
// do something
} finally {
jedis.del("lock-key");
}
}</pre><div class="contentsignin">登入後複製</div></div>
試想一下,某線程A獲取了鎖並且設定了過期時間為10s,然後在執行業務邏輯的時候耗費了15s,此時線程A獲取的鎖早已被Redis的過期機制自動釋放了
在線程A獲取鎖並經過10s之後,改鎖可能已經被其它線程獲取到了。當執行緒A執行完業務邏輯準備解鎖(
DEL 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
表示設定key時指定的唯一值。 由於Lua腳本的原子性,在Redis執行該腳本的過程中,其他客戶端的命令都需要等待該Lua腳本執行完才能執行。
為了防止多個執行緒同時執行業務程式碼,需要確保過期時間大於業務執行時間
#增加一個boolean類型的屬性
isOpenExpirationRenewal,用來標識是否開啟定時刷新過期時間在增加一個
#方法用於開啟刷新過期時間的執行緒
加鎖代碼在取得鎖定成功後將isOpenExpirationRenewal置為true,並且呼叫
方法,開啟刷新過期時間的執行緒解鎖碼增加一行程式碼,將isOpenExpirationRenewal屬性置為false,停止刷新過期時間的線程輪詢
獲取鎖定成功就會開啟一個定時任務,定時任務會定期檢查去續期
此定時調度每次呼叫的時間差是internalLockLeaseTime / 3
,也就10秒
預設情況下,加鎖的時間是30秒.如果加鎖的業務沒有執行完,那麼到30-10 = 20
秒的時候,就會進行一次續期,把鎖定重置成30秒
##RedLock
#在叢集中,主節點掛掉時,從節點會取而代之,客戶端上卻並沒有明顯感知。原先第一個客戶端在主節點中申請成功了一把鎖,但這把鎖還沒來得及同步到從節點,主節點突然掛掉了。然後從節點變成了主節點,這個新的節點內部沒有這個鎖,所以當另一個客戶端過來請求加鎖時,立即就批准了。這樣就會導致系統中同樣一把鎖被兩個客戶端同時持有,不安全性由此產生Redlock演算法就是為了解決這個問題
使用Redlock,需要提供多個Redis 實例,這些實例之前相互獨立沒有主從關係。同許多分散式演算法一樣,redlock 也使用大多數機制
set 成功,那就認為加鎖成功。釋放鎖定時,需要向所有節點發送 del 指令。不過Redlock 演算法還需要考慮出錯重試、時脈漂移等許多細節問題,同時因為
Redlock 需要向多個節點進行讀寫,這意味著相較於單一實例Redis 效能會下降一些
假設目前叢集有5 個節點,執行Redlock 演算法的客戶端依序執行下面各個步驟,來完成取得鎖定的操作
- 客戶端記錄當前系統時間,以毫秒為單位;
- 依序嘗試從5 個Redis 實例中,使用相同的key 取得鎖,當向Redis 請求取得鎖定時,客戶端應該設定一個網路連線和回應逾時時間,超時時間應該小於鎖的失效時間,避免因為網路故障出現的問題;
- 客戶端使用當前時間減去開始獲取鎖定時間就得到了獲取鎖使用的時間,當且僅當從半數以上的Redis 節點取得到鎖,並且當使用的時間小於鎖失效時間時,鎖才算獲取成功;
- 如果獲取到了鎖,key 的真正有效時間等於有效時間減去獲取鎖所使用的時間,減少超時的幾率;
- 如果獲取鎖定失敗,客戶端應該在所有的Redis 實例上進行解鎖,即使是上一步操作請求失敗的節點,防止因為服務端響應訊息丟失,但是實際數據添加成功導致的不一致。
Redisson 中,內建了對
RedLock 的實作
#https://redis.io/topics/distlockhttps:// github.com/redisson/redisson/wiki
RedLock問題:
RedLock 只是保證了鎖的高可用性,並沒有保證鎖的正確性RedLock 是一個嚴重依賴系統時脈的分散式系統
Martin 對RedLock 的批評:- #對於提升效率的場景下, RedLock 太重。
- 對於對正確性要求極高的場景下,RedLock 並不能保證正確性。
本文轉載自:https://juejin.cn/post/7018968452136173576作者:心懷遠方更多程式相關知識,請訪問:
編程視頻! !
以上是Redis中為什麼需要分散式鎖?如何實現?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

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

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

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

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

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

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

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

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

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

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

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