实战!Redis 分布式锁的 7 种实现方式
在实践中,有多种方式可以实现Redis分布式锁。以下是其中的七种方式,以及它们的示例代码:
- 使用 SETNX 命令
SETNX 是SET if Not eXists的缩写。当key不存在时,设置key的值。
def acquire_lock(conn, lock_name):
identifier = str(uuid.uuid4())
end = time.time() + 10 # 10秒超时
lock_name = 'lock:' + lock_name
while time.time() < end:
if conn.setnx(lock_name, identifier):
return identifier
time.sleep(0.001)
return False
def release_lock(conn, lock_name, identifier):
lock_name = 'lock:' + lock_name
with conn.pipeline() as pipe:
while True:
try:
pipe.watch(lock_name)
if pipe.get(lock_name) == identifier:
pipe.multi()
pipe.delete(lock_name)
pipe.execute()
return True
pipe.unwatch()
break
except redis.exceptions.WatchError:
pass
return False
- 使用 SET 命令的 EX 和 NX 选项
EX 选项用于设置键的过期时间,NX 选项表示只在键不存在时,才对键进行设置。
def acquire_lock(conn, lock_name):
identifier = str(uuid.uuid4())
lock_name = 'lock:' + lock_name
if conn.set(lock_name, identifier, nx=True, ex=10):
return identifier
return False
def release_lock(conn, lock_name, identifier):
lock_name = 'lock:' + lock_name
with conn.pipeline() as pipe:
while True:
try:
pipe.watch(lock_name)
if pipe.get(lock_name) == identifier:
pipe.multi()
pipe.delete(lock_name)
pipe.execute()
return True
pipe.unwatch()
break
except redis.exceptions.WatchError:
pass
return False
- 使用 Lua 脚本
Lua 脚本可以保证在执行过期设置和值比较的同时,键不会被其他客户端修改。
def acquire_lock(conn, lock_name):
identifier = str(uuid.uuid4())
lock_name = 'lock:' + lock_name
end = time.time() + 10
while time.time() < end:
if conn.eval(
"if redis.call('exists', KEYS[1]) == 0 then "
"redis.call('set', KEYS[1], ARGV[1]) "
"redis.call('expire', KEYS[1], 10) "
"return
评论已关闭