面试题答案
一键面试使用 SETNX 命令实现简单的分布式锁
- 加锁:
在 Redis 中,可以使用
SETNX key value
命令来实现加锁操作。SETNX
是SET if Not eXists
的缩写,即只有当指定的键不存在时,才会设置键的值。 示例代码(以 Python 和 redis - py 库为例):
import redis
r = redis.Redis(host='localhost', port=6379, db = 0)
lock_key = "my_distributed_lock"
lock_value = "unique_value"
if r.setnx(lock_key, lock_value):
print("获取锁成功")
else:
print("获取锁失败")
在这个示例中,setnx
方法尝试设置 lock_key
的值为 lock_value
。如果设置成功(返回 True
),说明获取锁成功;否则,说明锁已被其他进程持有。
- 解锁: 当任务完成后,需要释放锁,即删除对应的键。 示例代码:
if r.delete(lock_key):
print("释放锁成功")
else:
print("释放锁失败")
处理锁长时间未释放的情况
- 设置锁的过期时间:
为了防止锁长时间未释放,可以在加锁时给锁设置一个过期时间。在 Redis 2.6.12 版本之后,可以使用
SET key value NX EX seconds
命令,它等价于SETNX
加上EXPIRE
命令。 示例代码(Python):
if r.set(lock_key, lock_value, nx = True, ex = 10):
print("获取锁成功,设置过期时间为10秒")
else:
print("获取锁失败")
这里通过 ex
参数设置了锁的过期时间为 10 秒。如果在 10 秒内任务没有完成,锁会自动释放,其他进程可以获取锁。
- 使用看门狗机制:
对于一些长时间运行的任务,可以使用看门狗机制。即当获取锁成功后,启动一个后台线程,定期检查任务是否完成,如果任务还在运行,就延长锁的过期时间。
示例代码(以 Python 为例,使用
threading
模块实现简单的看门狗):
import threading
import time
def watchdog(redis_client, lock_key, lock_value, expire_time):
while True:
if redis_client.get(lock_key) == lock_value.encode('utf - 8'):
redis_client.expire(lock_key, expire_time)
time.sleep(expire_time // 3)
if r.set(lock_key, lock_value, nx = True, ex = 10):
print("获取锁成功")
# 启动看门狗线程
watch_dog_thread = threading.Thread(target = watchdog, args = (r, lock_key, lock_value, 10))
watch_dog_thread.daemon = True
watch_dog_thread.start()
else:
print("获取锁失败")
这个看门狗线程会每隔 expire_time // 3
秒检查一次锁是否还被当前进程持有,如果是,则延长锁的过期时间。