Spring Boot集成Redis + Lua脚本实现原子性操作:小白入门指南
一、为什么需要Lua脚本?
在分布式系统中,多个Redis命令的组合操作(如先查询后修改)可能因网络延迟、并发竞争导致数据不一致。Lua脚本可以将多个命令封装为一个原子操作,确保所有命令要么全部成功,要么全部失败 ,避免中间状态 优势 :
原子性:Redis单线程执行Lua脚本,执行期间不会被其他操作打断 减少网络开销:多个命令合并为一次请求
redis call__redis pcall__14">2. Lua脚本中 redis .call
和 redis .pcall
的区别
redis .call
:在执行 Redis 命令时,如果发生错误会抛出异常并终止脚本执行。redis .pcall
:在执行 Redis 命令时,即使发生错误也不会抛出异常,而是返回一个包含错误信息的表,脚本会继续执行
3. 在不同数据类型中的应用
1. String 类型
操作 Lua 脚本示例 参数说明 返回值 设置值 redis .call('SET', KEYS[1], ARGV[1])
KEYS[1]: key “OK” 获取值 redis .call('GET', KEYS[1])
KEYS[1]: key String 或 nil
(不存在时) 自增 redis .call('INCR', KEYS[1])
KEYS[1]: key 新整数(若 key 不存在则初始化为 1) 带过期时间设置 redis .call('SETEX', KEYS[1], ARGV[1], ARGV[2])
ARGV[1]: 秒,ARGV[2]: 值 “OK”
2. Hash 类型
操作 Lua 脚本示例 参数说明 返回值 设置字段值 redis .call('HSET', KEYS[1], ARGV[1], ARGV[2])
ARGV[1]: 字段名 1(新增)或 0(更新) 获取字段值 redis .call('HGET', KEYS[1], ARGV[1])
ARGV[1]: 字段名 String 或 nil
获取所有字段 redis .call('HGETALL', KEYS[1])
KEYS[1]: key Table(交替字段名和值) 删除字段 redis .call('HDEL', KEYS[1], ARGV[1])
ARGV[1]: 字段名 删除的字段数量
3. List 类型
操作 Lua 脚本示例 参数说明 返回值 左/右插入元素 redis .call('LPUSH', KEYS[1], ARGV[1])
ARGV[1]: 元素值 列表长度 获取范围元素 redis .call('LRANGE', KEYS[1], ARGV[1], ARGV[2])
ARGV[1]: 起始索引(0-based) Table(元素列表) 弹出元素 redis .call('LPOP', KEYS[1])
- 元素值或 nil
(空列表时)
4. Set 类型
操作 Lua 脚本示例 参数说明 返回值 添加元素 redis .call('SADD', KEYS[1], ARGV[1])
ARGV[1]: 元素值 新增元素数量 判断元素存在 redis .call('SISMEMBER', KEYS[1], ARGV[1])
ARGV[1]: 元素值 1(存在)或 0(不存在) 获取所有元素 redis .call('SMEMBERS', KEYS[1])
KEYS[1]: key Table(无序元素列表)
5. ZSet(有序集合)
操作 Lua 脚本示例 参数说明 返回值 添加带分数元素 redis .call('ZADD', KEYS[1], ARGV[1], ARGV[2])
ARGV[1]: 分数,ARGV[2]: 元素 新增元素数量 获取分数范围元素 redis .call('ZRANGEBYSCORE', KEYS[1], ARGV[1], ARGV[2])
ARGV[1]: 最小分数,ARGV[2]: 最大分数 Table(元素列表) 获取排名 redis .call('ZRANK', KEYS[1], ARGV[1])
ARGV[1]: 元素值 排名(从 0 开始)或 nil
6. 通用操作
操作 Lua 脚本示例 参数说明 返回值 判断 Key 是否存在 redis .call('EXISTS', KEYS[1])
KEYS[1]: key 1(存在)或 0(不存在) 设置过期时间 redis .call('EXPIRE', KEYS[1], ARGV[1])
ARGV[1]: 秒数 1(成功)或 0(Key 不存在) 删除 Key redis .call('DEL', KEYS[1])
KEYS[1]: key 删除的 Key 数量