面试题答案
一键面试判断写锁是否成功获取的方法
- SETNX(SET if Not eXists)命令:使用
SETNX key value
命令尝试设置一个特定的键值对,如果键不存在则设置成功并返回1,表示获取写锁成功;如果键已存在则返回0,表示获取写锁失败。示例代码(以Python和redis - py库为例):
import redis
r = redis.Redis(host='localhost', port=6379, db = 0)
lock_acquired = r.setnx('write_lock_key', 'lock_value')
if lock_acquired:
print('成功获取写锁')
else:
print('获取写锁失败')
- 使用Lua脚本:通过Lua脚本来确保获取锁操作的原子性。例如,在Lua脚本中使用
SETNX
命令,并且可以设置锁的过期时间,防止死锁。示例Lua脚本如下:
if (redis.call('SETNX', KEYS[1], ARGV[1]) == 1) then
redis.call('EXPIRE', KEYS[1], ARGV[2])
return 1
else
return 0
end
在Python中调用该Lua脚本的示例:
import redis
r = redis.Redis(host='localhost', port=6379, db = 0)
script = """
if (redis.call('SETNX', KEYS[1], ARGV[1]) == 1) then
redis.call('EXPIRE', KEYS[1], ARGV[2])
return 1
else
return 0
end
"""
sha = r.script_load(script)
lock_acquired = r.evalsha(sha, 1, 'write_lock_key', 'lock_value', 30)
if lock_acquired:
print('成功获取写锁')
else:
print('获取写锁失败')
获取失败的处理策略
- 重试策略:
- 固定时间间隔重试:在获取写锁失败后,等待一段固定的时间(例如100毫秒),然后再次尝试获取写锁。示例代码如下:
import time
import redis
r = redis.Redis(host='localhost', port=6379, db = 0)
while True:
lock_acquired = r.setnx('write_lock_key', 'lock_value')
if lock_acquired:
print('成功获取写锁')
break
else:
print('获取写锁失败,100毫秒后重试')
time.sleep(0.1)
- **指数退避重试**:每次重试的时间间隔以指数方式增长(例如从100毫秒开始,每次翻倍),避免在高并发场景下大量客户端同时重试导致的网络拥塞。示例代码如下:
import time
import redis
r = redis.Redis(host='localhost', port=6379, db = 0)
retry_delay = 0.1
while True:
lock_acquired = r.setnx('write_lock_key', 'lock_value')
if lock_acquired:
print('成功获取写锁')
break
else:
print(f'获取写锁失败,{retry_delay * 1000}毫秒后重试')
time.sleep(retry_delay)
retry_delay *= 2
- 队列等待策略:将获取写锁失败的请求放入一个队列(如Redis的List数据结构)中,当持有写锁的客户端释放锁后,从队列中取出一个请求并通知其重新尝试获取写锁。示例代码(使用Python和redis - py库模拟):
import redis
r = redis.Redis(host='localhost', port=6379, db = 0)
# 模拟获取写锁失败,将请求放入队列
r.rpush('write_lock_queue', 'client_id_1')
# 持有写锁的客户端释放锁后,从队列中取出一个请求
client_id = r.lpop('write_lock_queue')
if client_id:
# 通知该客户端重新尝试获取写锁
print(f'通知{client_id.decode()}重新尝试获取写锁')
- 放弃策略:在多次尝试获取写锁失败后,直接放弃获取写锁的操作,并向调用方返回错误信息,告知获取写锁失败。例如在上述重试代码中添加最大重试次数限制:
import time
import redis
r = redis.Redis(host='localhost', port=6379, db = 0)
retry_delay = 0.1
max_retries = 5
retries = 0
while retries < max_retries:
lock_acquired = r.setnx('write_lock_key', 'lock_value')
if lock_acquired:
print('成功获取写锁')
break
else:
print(f'获取写锁失败,{retry_delay * 1000}毫秒后重试')
time.sleep(retry_delay)
retry_delay *= 2
retries += 1
if retries == max_retries:
print('达到最大重试次数,放弃获取写锁')