面试题答案
一键面试合理设置锁过期时间的方法及理由
- 方法:
- 对于业务逻辑执行时间不确定的情况,一种可行的办法是设置一个相对较长的锁过期时间,同时在业务逻辑执行过程中,通过定期续约的方式来确保锁不会意外过期。这样既可以避免因业务执行时间长导致锁提前过期,其他进程获取到锁而产生数据不一致等问题,又能防止锁长时间不释放造成资源浪费。
- 理由:
- 如果设置的过期时间过短,业务还未执行完锁就过期了,那么其他进程可能会获取到锁,从而引发并发问题,比如重复执行相同的业务逻辑,导致数据不一致等。例如在电商下单场景中,可能会出现超卖现象。
- 若设置的过期时间过长,虽然能保证业务执行期间锁不会过期,但如果业务出现异常长时间阻塞,锁会一直占用,其他需要获取锁的进程会等待很久,降低了系统的并发性能,并且可能导致资源浪费。通过定期续约,可以在业务正常执行时保持锁的持有,而当业务结束后,锁能及时释放。
简单代码示例
import redis
import threading
import time
def acquire_lock(redis_client, lock_key, lock_value, expire_time):
# 使用setnx命令尝试获取锁
result = redis_client.setnx(lock_key, lock_value)
if result:
# 如果获取锁成功,设置过期时间
redis_client.expire(lock_key, expire_time)
return result
def release_lock(redis_client, lock_key, lock_value):
pipe = redis_client.pipeline()
while True:
try:
pipe.watch(lock_key)
if pipe.get(lock_key).decode('utf-8') == lock_value:
pipe.multi()
pipe.delete(lock_key)
pipe.execute()
return True
pipe.unwatch()
break
except redis.WatchError:
continue
return False
def renew_lock(redis_client, lock_key, lock_value, expire_time):
while True:
time.sleep(expire_time / 3) # 每隔一段时间续约
pipe = redis_client.pipeline()
while True:
try:
pipe.watch(lock_key)
if pipe.get(lock_key).decode('utf-8') == lock_value:
pipe.multi()
pipe.expire(lock_key, expire_time)
pipe.execute()
break
pipe.unwatch()
break
except redis.WatchError:
continue
def business_logic(redis_client, lock_key, lock_value, expire_time):
if acquire_lock(redis_client, lock_key, lock_value, expire_time):
try:
# 模拟不确定执行时间的业务逻辑
time.sleep(5)
print('业务逻辑执行中...')
# 启动续约线程
renew_thread = threading.Thread(target=renew_lock, args=(redis_client, lock_key, lock_value, expire_time))
renew_thread.start()
finally:
release_lock(redis_client, lock_key, lock_value)
print('锁已释放')
else:
print('获取锁失败')
if __name__ == '__main__':
r = redis.Redis(host='localhost', port=6379, db=0)
lock_key = 'distributed_lock'
lock_value = 'unique_value'
expire_time = 10 # 初始过期时间设置为10秒
business_logic(r, lock_key, lock_value, expire_time)
上述代码实现了使用Redis获取分布式锁、释放锁以及在业务执行过程中定期续约锁的功能。acquire_lock
函数用于尝试获取锁并设置过期时间,release_lock
函数用于释放锁,renew_lock
函数用于定期续约锁,business_logic
函数模拟了业务逻辑,在获取锁后执行不确定时长的业务,并启动续约线程,最后释放锁。