最近在学习黑马程序员的黑马点评项目中,使用到Lua脚本来确保程序的原子性。下面是对该Lua脚本使用的总结

Lua脚本内容:

以下是输出锁的脚本内容

1
2
3
4
5
6
7
8
--锁的线程标识KEY[1],线程标识ARGV[1]
-- 判断线程标识是否与锁中的线程标识一致
if(redis.call('get',KEY[1])==ARGV[1]) then
-- 一致,删除锁
return redis.call('del',KEY[1])
end
--不一致,直接返回
return 0

Lua脚本的加载:

1
2
3
4
5
6
private static final DefaultRedisScript<Long> UNLOCK_SCRIPT;
static {
UNLOCK_SCRIPT = new DefaultRedisScript<>();
UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));
UNLOCK_SCRIPT.setResultType(Long.class);
}

1. 静态变量声明

1
private static final DefaultRedisScript<Long> UNLOCK_SCRIPT;
  • 作用:声明一个静态不可变的DefaultRedisScript对象,泛型<Long>表示脚本返回值的类型为Long
  • 设计意图:
    • static final:确保全局唯一实例,避免重复创建对象。
    • 泛型Long:明确Lua脚本执行结果的类型,避免运行时类型转换错误。

2. 静态初始化块

1
2
3
static {
// 初始化逻辑
}
  • 执行时机:在类加载时执行,且仅执行一次。
  • 适用场景:适合初始化静态资源(如配置文件、脚本等),避免重复加载的开销。

3. 创建DefaultRedisScript实例

1
UNLOCK_SCRIPT = new DefaultRedisScript<>();
  • 对象功能:封装Lua脚本的元数据,包括脚本内容、SHA1哈希值和结果类型。
  • 底层机制:
    • 实现RedisScript接口,适配Spring的RedisTemplate执行逻辑。
    • 内部缓存脚本的SHA1哈希值,优先使用EVALSHA命令执行,减少网络传输。

4. 设置脚本路径

1
UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));
  • 参数解析:
    • ClassPathResource:Spring提供的资源加载器,从类路径(如resources/目录)读取文件。
    • unlock.lua:Lua脚本文件名,需与实际文件路径一致。
  • 加载过程:
    1. 读取unlock.lua文件内容。
    2. 计算脚本内容的SHA1哈希值(如a1b2c3d4...)。
    3. 将脚本和哈希值缓存到DefaultRedisScript对象中。

5. 设置结果类型

1
UNLOCK_SCRIPT.setResultType(Long.class);
  • 必要性:明确Redis返回值的Java类型,避免反序列化异常。
  • 匹配规则:
    • Lua返回整数(如10) → 映射为Long类型。
    • Lua返回nil → 映射为null
    • Lua返回布尔值 → 需设置为Boolean.class

Lua脚本的调用

1
2
3
4
5
6
7
8
9
    @Override
public void unlock() {
// 调用lua脚本
stringRedisTemplate.execute(
UNLOCK_SCRIPT,
Collections.singletonList(KEY_PREFIX + name),
ID_PREFIX+Thread.currentThread().getId()
);
}

execute()方法接受三个参数,第一个script,要执行的脚本;第二个需要传递给脚本的任何键,第三个,需要传递给脚本的任何args。