Python+Redis從零打造分布式鎖實(shí)戰(zhàn)示例
引言
在分布式系統(tǒng)中,多個(gè)節(jié)點(diǎn)可能需要訪問(wèn)同一共享資源,這就需要一種協(xié)調(diào)機(jī)制來(lái)保證在同一時(shí)刻只有一個(gè)節(jié)點(diǎn)進(jìn)行操作,這就是分布式鎖的作用。
1. 簡(jiǎn)單實(shí)現(xiàn)(基于SETNX命令)
使用setnx命令(set if not exists)嘗試設(shè)置一個(gè)key-value對(duì),如果key不存在,則設(shè)置成功并返回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. 改進(jìn):設(shè)置超時(shí)時(shí)間(expire命令)
為防止進(jìn)程崩潰導(dǎo)致鎖無(wú)法釋放,可以給鎖設(shè)置一個(gè)過(guò)期時(shí)間。
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. 進(jìn)一步改進(jìn):使用Lua腳本保證原子性
在釋放鎖的時(shí)候,需要確保刪除的是自己設(shè)置的鎖,且這個(gè)操作是原子的。可以使用Lua腳本來(lái)實(shí)現(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. 更完善的實(shí)現(xiàn),Redlock算法
Redlock算法的實(shí)現(xiàn)相對(duì)復(fù)雜,以下是一個(gè)簡(jiǎn)化的Python示例,展示了如何在多個(gè)Redis實(shí)例上嘗試獲取和釋放鎖。請(qǐng)注意,在實(shí)際應(yīng)用中,需要確保Redis實(shí)例之間的時(shí)間同步,并且考慮網(wǎng)絡(luò)延遲等因素。
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 = [] # 嘗試在每個(gè)Redis實(shí)例上獲取鎖 for master in self.masters: if master.set(resource_id, 'locked', nx=True, px=self.ttl): acquired_count += 1 locked_instances.append(master) # 如果獲得超過(guò)半數(shù)以上的鎖,則認(rèn)為成功獲取分布式鎖 if acquired_count > self.quorum: return locked_instances # 清除已獲取但未達(dá)到法定數(shù)量的鎖 for master in locked_instances: master.delete(resource_id) # 等待一段時(shí)間后重試 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腳本保證原子性地刪除鎖(與前面簡(jiǎn)單實(shí)現(xiàn)中的釋放鎖方法類(lèi)似) 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)] # 假設(shè)有兩個(gè)Redis實(shí)例 redlock = RedLock(masters, 10000) # 設(shè)置鎖的有效期為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)
此示例僅為了說(shuō)明Redlock算法的核心思想,并未涵蓋所有邊界條件和異常處理,如Redis實(shí)例宕機(jī)、網(wǎng)絡(luò)延遲導(dǎo)致時(shí)間不一致等情況,請(qǐng)?jiān)谏a(chǎn)環(huán)境中使用時(shí)結(jié)合實(shí)際情況進(jìn)行優(yōu)化和完善。
總結(jié)
通過(guò)以上介紹和示例代碼,我們了解了如何基于Python與Redis實(shí)現(xiàn)不同層次復(fù)雜度的分布式鎖。從簡(jiǎn)單的SETNX命令獲取鎖,到設(shè)置鎖的超時(shí)時(shí)間以防止進(jìn)程崩潰導(dǎo)致死鎖,再到利用Lua腳本保證釋放鎖操作的原子性,以及針對(duì)大規(guī)模分布式環(huán)境考慮采用Redlock算法提高系統(tǒng)的容錯(cuò)性和安全性。這些方法可以根據(jù)實(shí)際業(yè)務(wù)場(chǎng)景選擇合適的方式來(lái)實(shí)現(xiàn)分布式鎖,從而有效解決多節(jié)點(diǎn)環(huán)境下對(duì)共享資源的競(jìng)爭(zhēng)問(wèn)題。隨著技術(shù)的發(fā)展和需求的變化,分布式鎖的實(shí)現(xiàn)方式也將持續(xù)演進(jìn)和完善,為構(gòu)建更穩(wěn)定、高效的分布式系統(tǒng)提供有力支撐。
以上就是Python+Redis從零打造分布式鎖實(shí)戰(zhàn)示例的詳細(xì)內(nèi)容,更多關(guān)于Python Redis分布式鎖的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python下實(shí)現(xiàn)的RSA加密/解密及簽名/驗(yàn)證功能示例
這篇文章主要介紹了Python下實(shí)現(xiàn)的RSA加密/解密及簽名/驗(yàn)證功能,結(jié)合具體實(shí)例形式分析了Python中RSA加密、解密的實(shí)現(xiàn)方法及簽名、驗(yàn)證功能的使用技巧,需要的朋友可以參考下2017-07-07python爬蟲(chóng)---requests庫(kù)的用法詳解
requests是python實(shí)現(xiàn)的簡(jiǎn)單易用的HTTP庫(kù),使用起來(lái)比urllib簡(jiǎn)潔很多,這里就為大家分享一下2020-09-09如何解決PyCharm顯示:無(wú)效的Python?SDK
這篇文章主要介紹了在不同電腦之間傳輸Python項(xiàng)目時(shí)遇到的路徑問(wèn)題,并提供了解決方法,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2025-01-01linux系統(tǒng)使用python獲取內(nèi)存使用信息腳本分享
這篇文章主要介紹了linux系統(tǒng)使用python獲取內(nèi)存使用情況信息,大家參考使用吧2014-01-01使用Python實(shí)現(xiàn)視頻轉(zhuǎn)音頻與音頻轉(zhuǎn)文本
這篇文章主要為大家詳細(xì)介紹了使用Python實(shí)現(xiàn)視頻轉(zhuǎn)音頻與音頻轉(zhuǎn)文本的相關(guān)知識(shí),文中的示例代碼簡(jiǎn)潔易懂,有需要的小伙伴可以參考一下2024-02-02