Lua 脚本在 Redis 中的应用

在高并发场景下,Redis 不仅是一个缓存工具,更是实现分布式协调的重要组件。尤其是分布式锁,在防止资源争用、保证数据一致性方面有着关键作用。

实现分布式锁有多种方式,但无论是 SETNX 命令还是 Redisson 框架,背后都有一个绕不开的关键词------Lua 脚本。


一、什么是 Lua 脚本

Lua 是一种轻量、高效的脚本语言,Redis 原生支持用 Lua 脚本执行命令,并且可以保证脚本中的所有 Redis 操作是原子性的。

原子性:脚本中多条命令要么全部执行,要么全部不执行,中间不会被其他请求打断。

换句话说,Lua 脚本在 Redis 中相当于"批量命令的事务",非常适合需要多步逻辑且不能被打断的场景。


二、分布式锁的常见实现

1. 使用 SETNX 实现加锁

最简单的分布式锁实现:

shell 复制代码
SETNX lock_key unique_id
  • lock_key:锁的键名
  • unique_id:锁的唯一标识(防止误删他人锁)

加锁逻辑:

  • 如果 SETNX 返回 1,表示加锁成功
  • 如果返回 0,说明锁已经被别人持有

解锁逻辑:

lua 复制代码
if redis.call("get", "lock_key") == unique_id then
    redis.call("del", "lock_key")
end

2. 解锁的并发安全问题

如果用普通代码分两步实现解锁(先 GET 再 DEL):

java 复制代码
if (redisTemplate.opsForValue().get("lock_key").equals(unique_id)) {
    redisTemplate.delete("lock_key");
}

在高并发下可能发生:

  1. 线程 A GET 到锁是自己的
  2. 线程 B 抢占了锁(设置了新的 unique_id)
  3. 线程 A 执行 DEL,结果误删了线程 B 的锁

这就破坏了锁的正确性。


三、用 Lua 脚本保证解锁原子性

解决方法是用 Lua 脚本将"判断锁持有者"和"删除锁"两个步骤打包成一个原子操作:

lua 复制代码
if redis.call("get", KEYS[1]) == ARGV[1] then
    return redis.call("del", KEYS[1])
else
    return 0
end

Java 调用示例:

java 复制代码
String script =
    "if redis.call('get', KEYS[1]) == ARGV[1] then " +
    "return redis.call('del', KEYS[1]) else return 0 end";

Long result = (Long) redisTemplate.execute(
    new DefaultRedisScript<>(script, Long.class),
    Collections.singletonList("lock_key"),
    "unique_id"
);

if (result != null && result > 0) {
    System.out.println("解锁成功");
} else {
    System.out.println("解锁失败");
}

这样,无论并发多高,解锁过程都是安全的。


四、Redisson 的分布式锁

如果不想手写 Lua 脚本,可以直接使用 Redisson,它内部已经用 Lua 脚本实现了加锁、解锁的原子性,并且支持:

  • 可重入锁
  • 公平锁
  • 看门狗自动续期

示例:

java 复制代码
RLock lock = redissonClient.getLock("lock_key");
lock.lock();
try {
    // 业务逻辑
} finally {
    lock.unlock();
}

相关推荐
Wang's Blog19 分钟前
Java框架 SpringCloud 快速入门: 实现 Feign 最佳实践(抽取方式)
java·spring cloud
91刘仁德1 小时前
IP协议详解:从IP协议头到网段划分、路由与NAT
linux·服务器·网络·网络协议·tcp/ip
MandalaO_O1 小时前
IDEA 开发(快捷键 + 调试 + 序列化)
java·ide·intellij-idea
可乐鸡翅yeah_2 小时前
业务中 M3U8 水印相关坑,硬水印和动态水印区别
前端·网络·数据库·ffmpeg·m3u8在线
xiaoqiMikko2 小时前
JVM 线上排查实战(七):jps 看不见它、jstack 连不上它,可它明明活得好好的
java·jvm
lerhxx2 小时前
AI 应用如何高效优雅地恢复中断?—— "连接解耦 + 状态持久化"
前端·javascript
计算机魔术师2 小时前
METR 演示 AI 智能体如何篡改 Inspect 评估记录以掩盖不当行为
前端
前端snow3 小时前
ai agent --- 异步处理之 Rabbit MQ
前端
狼爷3 小时前
Rust/Go/Java/Python/PHP 大比拼:负载下后端框架到底差多少?
java·后端·编程语言
念何架构之路3 小时前
zap扩展生态与总结
java·前端·数据库