MST

星途 面试题库

面试题:Redis分布式锁多命令原子性:Lua脚本应用

使用Lua脚本来保障Redis分布式锁多命令操作的原子性是一种常见方法。请编写一个Lua脚本,实现一个简单的分布式锁加锁和解锁操作,要求考虑锁的持有标识、过期时间等因素,并解释脚本中每条命令的作用及为什么Lua脚本可以保证原子性。
13.9万 热度难度
数据库Redis

知识考点

AI 面试

面试题答案

一键面试

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

-- 解锁操作
-- KEYS[1] 是锁的键名
-- ARGV[1] 是锁的持有标识(例如UUID)
if (redis.call('GET', KEYS[1]) == ARGV[1]) then
    return redis.call('DEL', KEYS[1])
else
    return 0
end

脚本中命令作用解释

  1. 加锁部分
    • redis.call('SETNX', KEYS[1], ARGV[1])SETNX 命令是 SET if Not eXists 的缩写,即只有当指定的键不存在时,才会设置键的值。这里尝试将锁的键 KEYS[1] 设置为持有标识 ARGV[1],如果设置成功(返回1),表示获取到锁;如果设置失败(返回0),表示锁已被其他客户端持有。
    • redis.call('EXPIRE', KEYS[1], ARGV[2]):为获取到的锁设置过期时间 ARGV[2](单位:秒),防止锁在持有客户端出现异常时一直不释放。
  2. 解锁部分
    • redis.call('GET', KEYS[1]):获取锁的键 KEYS[1] 的值,用于判断当前锁是否是自己持有。
    • if (redis.call('GET', KEYS[1]) == ARGV[1]):判断当前锁的值是否与自己的持有标识 ARGV[1] 相等,如果相等,表示是自己持有的锁,可以进行解锁操作。
    • return redis.call('DEL', KEYS[1]):删除锁的键,释放锁。

Lua脚本保证原子性的原因

  1. Redis单线程模型:Redis是单线程执行命令的,在执行Lua脚本时,Redis会将整个Lua脚本作为一个整体来执行,期间不会被其他命令打断。这就保证了Lua脚本中多条命令的执行是原子性的,要么全部执行成功,要么全部不执行。
  2. Lua脚本的执行机制:Redis将Lua脚本的执行过程封装成一个原子操作,在脚本执行期间,Redis不会处理其他客户端的请求,直到脚本执行完毕。这样就避免了在执行多命令操作时,因为其他客户端的干扰而导致数据不一致的问题。