MST

星途 面试题库

面试题:Redis分布式锁多命令操作原子性:基本原理

在Redis分布式锁场景下,通常会涉及多个命令操作,如加锁、设置过期时间等。请阐述如何使用Redis的单命令特性来保障部分多命令操作的原子性,比如SETNX和EXPIRE的组合,它存在什么问题以及如何改进?
45.6万 热度难度
数据库Redis

知识考点

AI 面试

面试题答案

一键面试

使用Redis单命令特性保障多命令原子性

在Redis中,可以使用 SET key value [EX seconds] [PX milliseconds] [NX|XX] 这一单命令来替代 SETNXEXPIRE 的组合。该命令在设置键值对的同时可以设置过期时间,并且只有在键不存在时才会设置成功,从而保证了加锁和设置过期时间这两个操作的原子性。

SETNX 和 EXPIRE 组合的问题

  1. 原子性问题SETNXEXPIRE 是两个独立的命令,在高并发场景下,当 SETNX 执行成功后,还未来得及执行 EXPIRE 时,系统发生崩溃或其他异常情况,此时该锁没有设置过期时间,可能会导致死锁。
  2. 竞争条件:在多节点的分布式系统中,不同节点执行 SETNXEXPIRE 命令的时间差可能会引发竞争条件,导致锁的控制出现混乱。

改进方法

  1. 使用 SET 命令替代:如前文所述,使用 SET key value EX seconds NX 命令来替代 SETNXEXPIRE 的组合,确保加锁和设置过期时间的原子性。
  2. Lua 脚本:通过Lua脚本来保证多个命令的原子性。Lua脚本在Redis中是原子执行的,可以将多个相关命令封装在一个Lua脚本中,从而避免多命令执行过程中的竞争条件。例如:
-- 获取参数
local key = KEYS[1]
local value = ARGV[1]
local expireTime = ARGV[2]

-- 尝试加锁
if (redis.call('SETNX', key, value) == 1) then
    -- 加锁成功,设置过期时间
    redis.call('EXPIRE', key, expireTime)
    return 1
else
    -- 加锁失败
    return 0
end

在客户端使用时,通过 EVAL 命令执行该Lua脚本,这样即使在高并发场景下,也能保证加锁和设置过期时间操作的原子性。