- 重试机制设计
- 重试次数设定:
- 设定一个合理的最大重试次数,例如5 - 10次。如果超过这个次数仍然无法获取锁,则放弃本次获取锁的操作,并向调用方返回获取锁失败的信息。这可以避免无限重试消耗过多资源。
- 重试逻辑:
- 在每次尝试获取锁失败后,等待一段时间再进行下一次重试。这段等待时间需要根据具体业务场景和系统负载进行调整。
- 应用指数退避算法
- 基本原理:
- 指数退避算法是在每次重试时,将等待时间按照指数级增长。例如,初始等待时间设为100毫秒,下一次重试等待时间设为200毫秒,再下一次为400毫秒,以此类推(2的幂次方增长)。这样做的好处是随着重试次数增加,等待时间逐渐拉长,减少对系统的频繁请求压力,同时又给系统足够的时间来释放锁。
- 代码示例(以Python为例):
import redis
import time
redis_client = redis.Redis(host='localhost', port=6379, db = 0)
lock_key ='seckill_lock'
lock_value = 'unique_value'
max_retries = 5
base_delay = 0.1 # 初始延迟0.1秒
for retry in range(max_retries):
if redis_client.set(lock_key, lock_value, nx=True, ex = 10): # ex设置锁超时时间为10秒
try:
# 执行秒杀业务逻辑
print('获取锁成功,执行秒杀操作')
finally:
redis_client.delete(lock_key) # 释放锁
break
else:
delay = base_delay * (2 ** retry)
time.sleep(delay)
else:
print('获取锁失败')
- 考虑死锁问题
- 设置锁超时时间:
- 在获取锁时,通过
SET key value NX EX seconds
命令设置锁的超时时间(例如上面代码中的ex = 10
)。这样即使持有锁的进程出现异常没有主动释放锁,在超时后,锁也会自动释放,避免死锁。
- 检查锁的持有者:
- 在释放锁时,需要检查当前操作的锁是否是自己持有的。可以在获取锁时设置一个唯一标识(如上述代码中的
lock_value
),释放锁前先获取锁的值并与自己的标识比较,如果一致再进行释放操作,防止误释放其他进程持有的锁。
- 考虑锁超时问题
- 业务逻辑执行时间预估:
- 在设计业务逻辑时,尽量预估执行时间,并将锁的超时时间设置得略大于预估执行时间。例如,如果业务逻辑通常在5秒内完成,可以将锁超时时间设为10秒。
- 锁续期机制:
- 对于执行时间较长且不确定的业务,可以使用锁续期机制。例如在Java中可以使用Redisson的看门狗机制,它会在锁快要过期时,自动延长锁的有效期,保证业务能正常执行完而不会因为锁超时被其他进程获取锁。