MST

星途 面试题库

面试题:基于Redis脚本管理命令的分布式锁优化设计

在分布式系统中,使用Redis脚本管理命令实现一个高性能、高可用的分布式锁。要求详细描述设计思路,包括如何处理锁的竞争、锁的过期时间设置与续期、防止死锁以及应对网络分区等问题,并给出完整且优化后的Lua脚本示例及相应的客户端代码(语言不限)。
31.6万 热度难度
数据库Redis

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 锁的竞争处理:利用Redis的原子操作SETNX(SET if Not eXists)命令来尝试获取锁。只有当锁不存在时,SETNX才会成功设置锁的值,从而保证同一时间只有一个客户端能获取到锁。
  2. 锁的过期时间设置与续期:在获取锁时,同时设置一个过期时间,以防止因客户端崩溃或其他原因导致锁永远不释放。对于续期,可以使用一个后台线程定期检查锁的持有情况,如果还持有锁则延长过期时间,或者采用自动续期机制(如Redisson的看门狗机制)。
  3. 防止死锁:通过给每个锁设置唯一的标识(如UUID),客户端在释放锁时,首先检查当前锁的标识是否与自己持有的标识一致,只有一致时才释放锁,这样可以避免误释放其他客户端的锁。
  4. 应对网络分区:可以采用多副本的方式,将锁的数据复制到多个节点上。当出现网络分区时,只要大部分节点正常,锁服务依然可用。同时,客户端在获取锁时,需要尝试从多个节点获取,提高获取锁的成功率。

Lua脚本示例

-- KEYS[1] 是锁的键
-- ARGV[1] 是锁的值(例如UUID)
-- ARGV[2] 是锁的过期时间(秒)
if (redis.call('SETNX', KEYS[1], ARGV[1]) == 1) then
    redis.call('EXPIRE', KEYS[1], ARGV[2])
    return 1
else
    return 0
end

客户端代码示例(以Python为例)

import redis
import uuid

class DistributedLock:
    def __init__(self, host='localhost', port=6379, db=0):
        self.redis_client = redis.StrictRedis(host=host, port=port, db=db)
        self.lock_key = 'distributed_lock'
        self.lock_value = str(uuid.uuid4())
        self.expire_time = 10  # 锁过期时间,秒

    def acquire_lock(self):
        lua_script = """
        if (redis.call('SETNX', KEYS[1], ARGV[1]) == 1) then
            redis.call('EXPIRE', KEYS[1], ARGV[2])
            return 1
        else
            return 0
        end
        """
        result = self.redis_client.eval(lua_script, 1, self.lock_key, self.lock_value, self.expire_time)
        return result == 1

    def release_lock(self):
        lua_script = """
        if (redis.call('GET', KEYS[1]) == ARGV[1]) then
            return redis.call('DEL', KEYS[1])
        else
            return 0
        end
        """
        result = self.redis_client.eval(lua_script, 1, self.lock_key, self.lock_value)
        return result == 1


# 使用示例
lock = DistributedLock()
if lock.acquire_lock():
    try:
        print('获取到锁,执行任务...')
        # 执行具体任务
    finally:
        lock.release_lock()
        print('释放锁')
else:
    print('未获取到锁')