为什么使用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);