面试题答案
一键面试1. 分布式锁实现思路
使用Redis的SETNX(SET if Not eXists)命令来实现分布式锁。SETNX命令在指定的key不存在时,为key设置指定的值,若key已存在则不做任何操作。同时,为了处理锁的过期时间与业务执行时间的关系,我们需要在获取锁时设置一个合理的过期时间,并且在业务执行过程中,若发现锁即将过期,需要进行锁续约。
2. Redis操作
获取锁
import redis
redis_client = redis.StrictRedis(host='localhost', port=6379, db=0)
def acquire_lock(lock_key, lock_value, expire_time):
result = redis_client.set(lock_key, lock_value, nx=True, ex=expire_time)
return result
在上述代码中,set
方法的 nx=True
表示只有当 lock_key
不存在时才会设置成功,ex=expire_time
表示设置锁的过期时间。
释放锁
def release_lock(lock_key, lock_value):
pipe = redis_client.pipeline()
while True:
try:
pipe.watch(lock_key)
if pipe.get(lock_key) == lock_value.encode('utf-8'):
pipe.multi()
pipe.delete(lock_key)
pipe.execute()
return True
pipe.unwatch()
break
except redis.WatchError:
continue
return False
这里使用 watch
命令来监控 lock_key
,确保在删除锁之前,锁的值没有被其他客户端修改。如果锁的值与当前客户端设置的值相同,则删除锁。
锁续约
def renew_lock(lock_key, lock_value, expire_time):
pipe = redis_client.pipeline()
while True:
try:
pipe.watch(lock_key)
if pipe.get(lock_key) == lock_value.encode('utf-8'):
pipe.multi()
pipe.setex(lock_key, expire_time, lock_value)
pipe.execute()
return True
pipe.unwatch()
break
except redis.WatchError:
continue
return False
在业务执行过程中,通过 renew_lock
方法来延长锁的过期时间,确保业务完成之前锁不会过期。
3. 异常处理
获取锁异常
如果获取锁失败,可能是因为其他客户端已经获取了锁,此时可以选择等待一段时间后重试,或者直接返回失败信息给用户。例如:
max_retries = 3
retry_delay = 0.1
for i in range(max_retries):
if acquire_lock(lock_key, lock_value, expire_time):
break
time.sleep(retry_delay)
else:
# 获取锁失败处理逻辑
raise Exception("Failed to acquire lock")
释放锁异常
释放锁失败可能是因为锁已经过期或被其他客户端删除,这种情况下需要检查业务状态,确保商品没有超卖。如果业务已经完成,可以忽略释放锁失败的情况;如果业务未完成,需要根据具体业务逻辑进行回滚或重试。
锁续约异常
锁续约失败可能导致业务未完成锁就过期,从而引发超卖问题。因此,在锁续约失败时,需要尽快重新获取锁并继续执行业务。可以在业务执行过程中定时检查锁的剩余时间,提前进行锁续约操作。
4. 保障业务逻辑正确性
数据一致性
在分布式环境下,为了保障集群一致性,可以使用Redis的复制和持久化机制。通过配置主从复制,确保数据在多个节点之间同步;同时,启用AOF(Append - Only File)或RDB(Redis Database)持久化,防止数据丢失。
业务原子性
在获取锁之后,对商品库存的操作必须是原子性的。可以使用Redis的事务(multi
和 exec
)来确保多个操作要么全部成功,要么全部失败。例如:
def deduct_stock(stock_key, amount):
pipe = redis_client.pipeline()
pipe.watch(stock_key)
stock = int(pipe.get(stock_key))
if stock >= amount:
pipe.multi()
pipe.decrby(stock_key, amount)
pipe.execute()
return True
pipe.unwatch()
return False
在上述代码中,先使用 watch
监控库存键,然后获取库存并检查是否足够,若足够则使用事务减少库存。
通过以上方案,可以在高并发的电商秒杀系统中,有效地实现分布式锁,处理锁的过期时间与业务执行时间的关系,同时保障分布式环境下的集群一致性和业务逻辑的正确性。