redis分布式锁原理
在分布式系统中,多个节点可能同时访问共享资源,这可能导致数据不一致问题。为了解决这个问题,可以使用Redis分布式锁来协调不同节点对共享资源的访问。通过Redis实现分布式锁的核心思想是利用其单线程特性和原子操作,确保只有一个客户端能够获取锁。
以下是几种实现Redis分布式锁的思路和代码示例。
1. 简单实现:SETNX命令
Redis的SETNX
命令(即SET if Not eXists)可以在键不存在时设置键值。我们可以利用这个特性来实现一个简单的分布式锁。
python
import redis
import time</p>
<p>client = redis.StrictRedis(host='localhost', port=6379, db=0)</p>
<p>def acquire<em>lock(lock</em>key, timeout=10):
end<em>time = time.time() + timeout
while time.time() < end</em>time:
if client.setnx(lock<em>key, "locked"):
# 设置过期时间,防止死锁
client.expire(lock</em>key, 5)
return True
time.sleep(0.1) # 防止忙等待
return False</p>
<p>def release<em>lock(lock</em>key):
client.delete(lock_key)</p>
<h1>使用锁</h1>
<p>if acquire<em>lock("my</em>lock"):
try:
print("Lock acquired, performing critical operation...")
# 执行关键操作
finally:
release<em>lock("my</em>lock")
else:
print("Failed to acquire lock.")
2. 基于Lua脚本的改进实现
为了提高原子性,避免在获取锁和设置过期时间之间出现竞争条件,可以使用Redis的Lua脚本来一次性完成这两个操作。
lua
-- Lua脚本:尝试获取锁
local key = KEYS[1]
local value = ARGV[1]
local expiration = tonumber(ARGV[2])</p>
<p>if redis.call("setnx", key, value) == 1 then
redis.call("expire", key, expiration)
return 1
else
return 0
end
在Python中调用该Lua脚本:
python
import redis
import time
import uuid</p>
<p>client = redis.StrictRedis(host='localhost', port=6379, db=0)</p>
<p>lock_script = """
local key = KEYS[1]
local value = ARGV[1]
local expiration = tonumber(ARGV[2])</p>
<p>if redis.call("setnx", key, value) == 1 then
redis.call("expire", key, expiration)
return 1
else
return 0
end
"""</p>
<p>def acquire<em>lock</em>lua(lock<em>key, timeout=10):
identifier = str(uuid.uuid4())
end</em>time = time.time() + timeout
while time.time() < end<em>time:
if client.eval(lock</em>script, 1, lock_key, identifier, 5):
return identifier
time.sleep(0.1)
return None</p>
<p>def release<em>lock</em>lua(lock<em>key, identifier):
pipe = client.pipeline(True)
while True:
try:
pipe.watch(lock</em>key)
if pipe.get(lock<em>key) == identifier:
pipe.multi()
pipe.delete(lock</em>key)
pipe.execute()
return True
pipe.unwatch()
break
except redis.WatchError:
continue
return False</p>
<h1>使用锁</h1>
<p>identifier = acquire<em>lock</em>lua("my<em>lock</em>lua")
if identifier:
try:
print("Lock acquired via Lua, performing critical operation...")
# 执行关键操作
finally:
release<em>lock</em>lua("my<em>lock</em>lua", identifier)
else:
print("Failed to acquire lock via Lua.")
3. Redlock算法
Redlock是一种更复杂的分布式锁算法,适用于跨多个Redis实例的情况。它通过在多个Redis实例上尝试获取锁,并根据多数派原则决定是否成功获取锁。
python
from redlock import Redlock</p>
<p>dlm = Redlock([{"host": "localhost", "port": 6379, "db": 0},
{"host": "localhost", "port": 6380, "db": 0},
{"host": "localhost", "port": 6381, "db": 0}])</p>
<p>lock = dlm.lock("resource_name", 1000) # 尝试获取锁,有效期1000ms</p>
<p>if lock:
try:
print("Lock acquired using Redlock, performing critical operation...")
# 执行关键操作
finally:
dlm.unlock(lock)
else:
print("Failed to acquire lock using Redlock.")
以上三种方法分别展示了从简单到复杂的不同Redis分布式锁实现方式。选择哪种方式取决于具体的应用场景和需求。
(本文来源:nzw6.com)