借鉴:https://blog.youkuaiyun.com/qq_38787653/article/details/118619734
一:Rdis使用lua脚本的作用
- 减少网络开销,在Lua脚本中可以把多个命令放在同一个脚本中运行
- 原子操作,redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。换句话说,编写脚本的过程中无需担心会出现竞态条件
- 复用性,客户端发送的脚本会永远存储在redis中,这意味着其他客户端可以复用这一脚本来完成同样的逻辑
二:使用的案例
1.根据业务编写lua脚本 XX.lua
local current = redis.call('GET', KEYS[1])
if (current == ARGV[1]) then
redis.call('SET', KEYS[1], ARGV[2])
redis.call('EXPIRE', KEYS[1], ARGV[3])
return true
end
return false
Lua可以使用redis.call和redis.pcall函数实现对Redis的调用,redis.call和 redis.pcall的不同在于:
如果redis.call执行失败,那么脚本执行结束会直接返 回错误
而redis.pcall会忽略错误继续执行脚本
2.实例化
@Configuration
public class RedisLuaScriptConfig {
/**
* 实例化lua 脚本 cas 操作
*/
@Bean
public RedisScript<Boolean> compereAndSetAndExpire() {
return RedisScript.of(new ClassPathResource("redis/compereAndSetAndExpire.lua"), Boolean.class);
}
}
3.调用
public class XX{
@Resource(name = "compereAndSetAndExpire")
RedisScript<Boolean> compereAndSetAndExpire;
@Autowired
private RedisTemplate<String, String> redisTemplate;
public void XX {
// 1脚本,2, key 3, 期望值, 4, 要设置值 5过期时长 单位秒
Boolean red = Optional.ofNullable(redisTemplate.execute(compereAndSetAndExpire, Lists.newArrayList(key), "8", "10",60000)).orElse(false);
}
}
三:保证原子性的原理
- 一个lua 脚本会锁住整个 redis-server ,其他所有 redis-client (包括命令行 jedis lettuce redission) 此时都无法的对 redis-server 发送命令 (证明方法:lua脚本写一个死循环,其他各种各样的redis客户端就连不上了,因为redis是单线程处理客户端请求) :缺点就是影响性能
四:存在的问题和解决方法
问题:脚本随便是一次性给redis执行,但是还是一条一条命令执行的,某个命令还是可能执行失败
解决方法:可以执行脚本之前,先获取到redis中key对应的值。如果执行脚本报错了就把key的值设置回原来的