这次改造的核心目标是:把「解锁时的非原子操作」改成「原子操作」,彻底解决分布式锁的「判断 + 删除」时序问题

一、先回顾:旧版解锁的致命缺陷

旧版解锁逻辑:

public void unlock() {
    // 1. 获取当前线程标识
    String threadId = ID_PREFIX + Thread.currentThread().getId();
    // 2. 从Redis获取锁标识(get操作)
    String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + name);
    // 3. 判断标识是否一致
    if (threadId.equals(id)) {
        // 4. 删除锁(delete操作)
        stringRedisTemplate.delete(KEY_PREFIX + name);
    }
}

问题出在:getdelete是两次独立的 Redis 命令,中间存在时间窗口

二、用 Lua 脚本实现原子解锁

Lua 脚本的特性:Redis 会将整个脚本作为一个原子操作执行,中间不会被其他命令打断。

  1. 新增 Lua 脚本配置

// 定义解锁脚本
private static final DefaultRedisScript<Long> UNLOCK_SCRIPT;
static {
    UNLOCK_SCRIPT = new DefaultRedisScript<>();
    // 加载resources下的unlock.lua文件
    UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));
    // 指定脚本返回值类型
    UNLOCK_SCRIPT.setResultType(Long.class);
}
  • DefaultRedisScript:Spring Data Redis 提供的脚本执行器

  • static静态块:类加载时就加载脚本,避免每次解锁都重新读取文件

  • unlock.lua:存储解锁逻辑的 Lua 脚本文件

  1. 替换 Java 解锁逻辑为 Lua 脚本调用

@Override
public void unlock() {
    // 调用Lua脚本执行解锁
    stringRedisTemplate.execute(
        UNLOCK_SCRIPT, // 脚本对象
        Collections.singletonList(KEY_PREFIX + name), // KEYS参数:锁的key
        ID_PREFIX + Thread.currentThread().getId()    // ARGV参数:当前线程标识
    );
}
1、stringRedisTemplate.execute(...)
  • 作用:Spring 提供的执行 Redis Lua 脚本的核心方法

  • 特点:Redis 会把整个脚本当作一条原子命令执行,不会被其他命令打断

2、UNLOCK_SCRIPT
  • 提前加载好的Lua 解锁脚本

3. Collections.singletonList(KEY_PREFIX + name)
  • 传给 Lua 的KEYS 数组参数

  • 内容:lock:order:userId(锁的 Redis Key)

  • 必须用 List 集合,是 Redis 脚本的参数规范

4. ID_PREFIX + Thread.currentThread().getId()
  • 传给 Lua 的ARGV 参数

  • 内容:当前线程的唯一标识(UUID + 线程 ID)

  • 用于 Lua 脚本判断:这把锁是不是当前线程加的

三、unlock.lua脚本内容

-- 比较线程标示与锁中的标示是否一致
if (redis.call('get', KEYS[1]) == ARGV[1]) then
    -- 释放锁:标识一致,删除锁
    return redis.call('del', KEYS[1])
end
-- 标识不一致,直接返回
return 0
  • redis.call('get', KEYS[1]):获取锁的标识(原子操作)

  • == ARGV[1]:和当前线程的标识对比(原子操作)

  • redis.call('del', KEYS[1]):如果一致,删除锁(原子操作)

总结:通过 Lua 脚本将「判断锁标识 + 删除锁」合并为 Redis 原子操作,彻底解决旧版 Java 解锁逻辑中「非原子操作导致的锁误删问题」,让分布式锁的解锁过程 100% 安全可靠。

更多推荐