MST
星途 面试题库

面试题:缓存设计之Redis分布式锁的高可用与性能优化

当使用Redis实现分布式锁时,在高并发场景下,如何保证锁的可靠性与性能?比如面对网络抖动、节点故障等情况,你会采取哪些措施来优化和稳定分布式锁的运行?
25.3万 热度难度
后端开发缓存设计

知识考点

AI 面试

面试题答案

一键面试

保证锁可靠性的措施

  1. 使用SETNX + EX(PX)组合:使用SET key value NX EX seconds(或SET key value NX PX milliseconds)命令。SETNX确保只有在键不存在时才设置值,实现互斥;EX(或PX)设置锁的过期时间,防止死锁。
  2. UUID防误解锁:每个获取锁的客户端生成一个唯一的UUID作为锁的值。解锁时,先检查锁的值是否与自己的UUID一致,只有一致时才执行删除操作,防止误删其他客户端的锁。可以使用Lua脚本来保证检查和删除操作的原子性,如:
if redis.call("GET", KEYS[1]) == ARGV[1] then
    return redis.call("DEL", KEYS[1])
else
    return 0
end
  1. 多节点部署(Redlock算法):对于网络抖动和节点故障,采用Redlock算法。它基于多个独立的Redis节点,客户端需在大多数(N/2 + 1,N为节点总数)节点上成功获取锁才算获取成功。释放锁时,需要向所有节点发送释放锁的请求。示例代码(Python + redlock-py库):
from redlock import Redlock

# 定义Redis节点
redis_client1 = Redis(host='localhost', port=6379)
redis_client2 = Redis(host='localhost', port=6380)
redis_client3 = Redis(host='localhost', port=6381)

redlock = Redlock([redis_client1, redis_client2, redis_client3])
lock = redlock.lock("resource_name", 1000)  # 尝试获取锁,锁持有时间1000毫秒
if lock:
    try:
        # 执行业务逻辑
        pass
    finally:
        redlock.unlock(lock)  # 释放锁

优化性能的措施

  1. 减少网络开销:尽量减少获取和释放锁的网络请求次数。可以批量处理一些操作,避免频繁的锁获取和释放。
  2. 合理设置过期时间:根据业务实际情况合理设置锁的过期时间,避免过期时间过长导致性能下降,或过短导致锁频繁失效。对于一些复杂且耗时较长的业务,可以在锁快要过期时,使用Lua脚本进行续期操作。
  3. 使用连接池:在客户端使用连接池来管理与Redis的连接,减少连接创建和销毁的开销,提高并发性能。例如在Java中使用Jedis连接池:
JedisPoolConfig config = new JedisPoolConfig();
JedisPool jedisPool = new JedisPool(config, "localhost", 6379);
try (Jedis jedis = jedisPool.getResource()) {
    String result = jedis.set("lock_key", "lock_value", "NX", "EX", 10);
    if ("OK".equals(result)) {
        // 获取锁成功,执行业务逻辑
    }
} catch (Exception e) {
    e.printStackTrace();
} finally {
    jedisPool.close();
}
  1. 采用异步操作:在释放锁等操作时,可以考虑使用异步方式,减少主线程等待时间,提高整体性能。例如在Node.js中使用asyncawait结合redis模块实现异步释放锁:
const redis = require('redis');
const client = redis.createClient();

async function releaseLock(key, value) {
    const script = `if redis.call("GET", KEYS[1]) == ARGV[1] then return redis.call("DEL", KEYS[1]) else return 0 end`;
    return new Promise((resolve, reject) => {
        client.eval(script, 1, key, value, (err, reply) => {
            if (err) {
                reject(err);
            } else {
                resolve(reply);
            }
        });
    });
}

// 获取锁并执行业务逻辑后,异步释放锁
releaseLock('lock_key', 'lock_value').then(() => {
    console.log('锁已成功释放');
}).catch((err) => {
    console.error('释放锁失败', err);
});