面试题答案
一键面试1. 锁的获取
为确保分布式锁不出现锁泄露,在获取锁时可采用以下措施:
- 使用唯一标识:每个获取锁的请求都关联一个唯一标识(如UUID),用于标识锁的持有者。这样即使节点故障,后续也能通过该标识判断锁是否仍被合法持有。
- 设置合理的过期时间:为防止节点故障后锁永远无法被释放,需要设置一个合理的过期时间。但过期时间设置需权衡,过短可能导致业务未完成锁就过期,过长则可能影响系统可用性。可以根据业务执行时间的统计数据,设置一个略长于平均执行时间的过期时间。
2. 锁的释放
- 验证释放者:在释放锁时,首先验证当前释放锁的标识是否与获取锁时的标识一致。只有持有锁的节点才能释放锁,避免其他节点误释放。
- 采用原子操作:使用分布式系统提供的原子操作来释放锁,如Redis的
DEL
命令在单线程环境下是原子的,保证释放锁的操作不会被打断。
3. 故障恢复
- 自动续约:对于长时间运行的任务,可以在锁快要过期时,由持有锁的节点自动进行续约操作,延长锁的持有时间。如使用Redis的
EXPIRE
命令来更新锁的过期时间。 - 故障检测与转移:系统应具备检测节点故障的机制,当检测到持有锁的节点故障时,通过一定的选举算法(如Raft算法的简化版),重新分配锁的持有权给其他健康节点。
代码示例(以Python和Redis为例)
import redis
import uuid
class DistributedLock:
def __init__(self, redis_client, lock_key, lock_timeout=10):
self.redis_client = redis_client
self.lock_key = lock_key
self.lock_timeout = lock_timeout
self.lock_value = str(uuid.uuid4())
def acquire(self):
# 使用SETNX(SET if Not eXists)来获取锁,同时设置过期时间
result = self.redis_client.set(self.lock_key, self.lock_value, ex=self.lock_timeout, nx=True)
return result
def release(self):
pipe = self.redis_client.pipeline()
# 验证锁的持有者
while True:
try:
pipe.watch(self.lock_key)
if pipe.get(self.lock_key).decode('utf-8') == self.lock_value:
pipe.multi()
pipe.delete(self.lock_key)
pipe.execute()
return True
else:
return False
except redis.WatchError:
continue
# 示例使用
if __name__ == "__main__":
r = redis.Redis(host='localhost', port=6379, db=0)
lock = DistributedLock(r, 'test_lock')
if lock.acquire():
try:
print('Lock acquired, doing some work...')
# 模拟业务操作
finally:
lock.release()
print('Lock released')
else:
print('Failed to acquire lock')
在上述代码中,DistributedLock
类实现了一个简单的分布式锁。acquire
方法使用Redis的SET
命令并带上nx=True
选项来尝试获取锁,同时设置过期时间。release
方法使用Redis的WATCH
和MULTI
命令来确保只有锁的持有者才能释放锁,避免误释放。