Redis 的 Lua 脚本功能是什么?如何使用?

276 阅读2分钟

为什么使用lua脚本?

  • Redis 的 Lua 脚本功能用于在服务器端原子性地执行一系列命令。使用 Lua 脚本可以减少网络往返, 提升性能, 并确保操作的原子性。
    • 原子性: 整个脚本作为一个单元执行, 确保操作的完整性。
    • 减少网络延迟: 将多个命令组合在一个脚本中, 减少客户端与服务器之间的通信次数。
    • 复杂操作: 可以在脚本中执行复杂的逻辑和操作。

lua脚本使用方法

  • 编写 Lua 脚本: Lua 脚本可以直接在 Redis CLI 中输入, 也可以保存在文件中。
  • 使用 EVAL 命令:
    • 语法: EVAL script numkeys key [key ...] arg [arg ...]
    • script: Lua 脚本代码。
    • numkeys: 脚本中涉及的键的数量。
    • key [key ...]: 脚本中使用的键。
    • arg [arg ...]: 传递给脚本的其他参数。
    • 示例:
EVAL "return redis.call('set', KEYS[1], ARGV[1])" 1 mykey myvalue

使用 EVALSHA 命令

  • 在执行过一次 EVAL 后, 可以通过脚本的 SHA1 校验和再次执行, 节省传输脚本的时间;
  • 语法与 EVAL 类似, 但需要提供 SHA1 校验和;

注意事项

  • 脚本执行时间: 脚本应尽量简短, 以避免阻塞 Redis。
  • 数据访问限制: Lua 脚本只能访问传入的键, 不能动态访问 Redis 中的其他数据。
  • 调试: 可以通过 redis.log() 函数在 Redis 日志中记录调试信息。

分布式锁lua脚本

local key = KEYS[1]
local expire = ARGV[1]
local val = ARGV[2]


if  redis.call('setnx', key, val) == 1 then
    return redis.call('expire', key, expire)
else
    return 0
end
local key = KEYS[1]
local val = ARGV[1]

if  redis.call('exists', key) == 0 then
    return 1
end

-- 防止 错误释放.
if  redis.call('get', key) == val then
    return redis.call('del', key)
else
    return 0
end

java使用

lua脚本加载&缓存

@Override
public void afterPropertiesSet() throws Exception {
    Assert.notNull(getRedisTemplate(), "RedisTemplate is required.");
    synchronized (scriptMap) {
        if (CollectionUtils.isEmpty(scriptMap)) {
            scriptMap.put(lock, loadScript(lockScriptPath));
            scriptMap.put(unlock, loadScript(unlockScriptPath));
        }
    }
}


private DefaultRedisScript<Long> loadScript(String scriptPath) {
    DefaultRedisScript<Long> script = new DefaultRedisScript<>();
    script.setScriptSource(new ResourceScriptSource(new ClassPathResource(scriptPath)));
    script.setResultType(Long.class);
    return script;
}

lua脚本执行

redisTemplate.execute(scriptMap.get(lock), keys, args);