Python+Redis從零打造分布式鎖實戰(zhàn)示例
引言
在分布式系統(tǒng)中,多個節(jié)點可能需要訪問同一共享資源,這就需要一種協(xié)調機制來保證在同一時刻只有一個節(jié)點進行操作,這就是分布式鎖的作用。
1. 簡單實現(xiàn)(基于SETNX命令)
使用setnx命令(set if not exists)嘗試設置一個key-value對,如果key不存在,則設置成功并返回1,表示獲取到了鎖。
import redis
import time
def acquire_lock(redis_client, lock_key):
identifier = str(time.time()) + str(id(threading.current_thread()))
acquired = redis_client.setnx(lock_key, identifier)
return acquired
def release_lock(redis_client, lock_key, identifier):
pipe = redis_client.pipeline(True)
while True:
try:
pipe.watch(lock_key)
if pipe.get(lock_key) == identifier:
pipe.multi()
pipe.delete(lock_key)
pipe.execute()
return True
pipe.unwatch()
break
except redis.exceptions.WatchError:
pass
return False
# 使用示例
r = redis.Redis(host='localhost', port=6379, db=0)
lock_key = 'mylock'
if acquire_lock(r, lock_key):
print("Lock acquired")
# 執(zhí)行臨界區(qū)代碼...
release_lock(r, lock_key, identifier)2. 改進:設置超時時間(expire命令)
為防止進程崩潰導致鎖無法釋放,可以給鎖設置一個過期時間。
def acquire_lock_with_timeout(redis_client, lock_key, expire_time):
identifier = str(time.time()) + str(id(threading.current_thread()))
while True:
result = redis_client.set(lock_key, identifier, nx=True, ex=expire_time)
if result:
return identifier
time.sleep(0.1)
# 使用示例不變,但在acquire_lock_with_timeout函數(shù)中增加了expire_time參數(shù)3. 進一步改進:使用Lua腳本保證原子性
在釋放鎖的時候,需要確保刪除的是自己設置的鎖,且這個操作是原子的。可以使用Lua腳本來實現(xiàn)。
RELEASE_LOCK_SCRIPT = """
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
"""
def release_lock LUA(redis_client, lock_key, identifier):
release_script = redis_client.register_script(RELEASE_LOCK_SCRIPT)
released = release_script(keys=[lock_key], args=[identifier])
return bool(released)
# 使用示例不變4. 更完善的實現(xiàn),Redlock算法
Redlock算法的實現(xiàn)相對復雜,以下是一個簡化的Python示例,展示了如何在多個Redis實例上嘗試獲取和釋放鎖。請注意,在實際應用中,需要確保Redis實例之間的時間同步,并且考慮網(wǎng)絡延遲等因素。
import redis
import time
class RedLock:
def __init__(self, masters, ttl):
self.masters = [redis.Redis(host=host, port=port, db=db) for host, port, db in masters]
self.quorum = len(self.masters) // 2 + 1
self.ttl = ttl
def lock(self, resource_id):
expiration = time.time() + self.ttl
while time.time() < expiration:
acquired_count = 0
locked_instances = []
# 嘗試在每個Redis實例上獲取鎖
for master in self.masters:
if master.set(resource_id, 'locked', nx=True, px=self.ttl):
acquired_count += 1
locked_instances.append(master)
# 如果獲得超過半數(shù)以上的鎖,則認為成功獲取分布式鎖
if acquired_count > self.quorum:
return locked_instances
# 清除已獲取但未達到法定數(shù)量的鎖
for master in locked_instances:
master.delete(resource_id)
# 等待一段時間后重試
time.sleep(0.1)
raise Exception("Could not acquire lock")
def unlock(self, resource_id, masters_with_lock):
for master in masters_with_lock:
# 使用Lua腳本保證原子性地刪除鎖(與前面簡單實現(xiàn)中的釋放鎖方法類似)
RELEASE_LOCK_SCRIPT = """
if redis.call("get", KEYS[1]) == "locked" then
return redis.call("del", KEYS[1])
else
return 0
end
"""
release_script = master.register_script(RELEASE_LOCK_SCRIPT)
release_script(keys=[resource_id])
# 示例用法:
masters = [('localhost', 6379, 0), ('localhost', 6380, 0)] # 假設有兩個Redis實例
redlock = RedLock(masters, 10000) # 設置鎖的有效期為10秒
resource_id = 'my_resource'
try:
masters_with_lock = redlock.lock(resource_id)
print(f"Acquired Redlock on resource {resource_id}")
# 執(zhí)行臨界區(qū)代碼...
finally:
redlock.unlock(resource_id, masters_with_lock)此示例僅為了說明Redlock算法的核心思想,并未涵蓋所有邊界條件和異常處理,如Redis實例宕機、網(wǎng)絡延遲導致時間不一致等情況,請在生產(chǎn)環(huán)境中使用時結合實際情況進行優(yōu)化和完善。
總結
通過以上介紹和示例代碼,我們了解了如何基于Python與Redis實現(xiàn)不同層次復雜度的分布式鎖。從簡單的SETNX命令獲取鎖,到設置鎖的超時時間以防止進程崩潰導致死鎖,再到利用Lua腳本保證釋放鎖操作的原子性,以及針對大規(guī)模分布式環(huán)境考慮采用Redlock算法提高系統(tǒng)的容錯性和安全性。這些方法可以根據(jù)實際業(yè)務場景選擇合適的方式來實現(xiàn)分布式鎖,從而有效解決多節(jié)點環(huán)境下對共享資源的競爭問題。隨著技術的發(fā)展和需求的變化,分布式鎖的實現(xiàn)方式也將持續(xù)演進和完善,為構建更穩(wěn)定、高效的分布式系統(tǒng)提供有力支撐。
以上就是Python+Redis從零打造分布式鎖實戰(zhàn)示例的詳細內容,更多關于Python Redis分布式鎖的資料請關注腳本之家其它相關文章!
相關文章
教你用Django將前端的數(shù)據(jù)存入Mysql數(shù)據(jù)庫
這篇文章主要給大家介紹了關于如何用Django將前端的數(shù)據(jù)存入Mysql數(shù)據(jù)庫的相關資料,文中通過圖文以及示例代碼介紹的非常詳細,對大家學習或者使用Django具有一定的參考學習價值,需要的朋友可以參考下2021-11-11
python?plotly設置go.Scatter為實線實例
這篇文章主要為大家介紹了python?plotly設置go.Scatter為實線線條的樣式實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10
Python深度學習pytorch神經(jīng)網(wǎng)絡Dropout應用詳解解
這篇文章主要為大家介紹了Python深度學習中關于pytorch神經(jīng)網(wǎng)絡Dropout的應用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步2021-10-10
Python打包編譯工具Pyinstaller與Nuitka特性超詳細對比
應用程序開發(fā)完畢,一般都需要對程序進行打包后發(fā)布,除開源項目外,很少有發(fā)布源碼的,這篇文章主要介紹了Python打包編譯工具Pyinstaller與Nuitka特性對比的相關資料,需要的朋友可以參考下2026-03-03
Python數(shù)據(jù)清洗之利用pandas篩選數(shù)據(jù)詳解
這篇文章主要介紹了Python數(shù)據(jù)清洗之利用pandas篩選數(shù)據(jù)詳解,Pandas是一個用于數(shù)據(jù)分析和處理的Python庫,它提供了高效的數(shù)據(jù)結構和數(shù)據(jù)分析工具,使得數(shù)據(jù)的清洗、轉換、分析和可視化變得更加容易和靈活,需要的朋友可以參考下2023-08-08

