脚本的原子性与阻塞代价
Lua 脚本在 Redis 中是原子执行的:整段脚本运行期间,服务器不会处理其他命令。这带来强一致性,也带来风险——脚本写得越重,阻塞时间越长,所有客户端一起等。lua-time-limit 默认 5000 毫秒,但它只用于打日志和让客户端感知超时,并不会强制终止执行中的脚本。超过该时间后,其他客户端会收到 BUSY 错误,此时只剩两条处置路径:脚本尚未写入数据时可以 SCRIPT KILL 终止,已经写入就只能停机评估重放。
基本信息
| 默认限制 | lua-time-limit 5000 毫秒 |
|---|---|
| 超时表现 | 其他客户端收到 BUSY 错误 |
| 可执行操作 | SCRIPT KILL(仅未写数据时)、SHUTDOWN NOSAVE |
| 复制方式 | 5.0 起的默认效果复制以数据变更为准 |
| 脚本管理 | SCRIPT LOAD / SCRIPT EXISTS / SCRIPT FLUSH |
典型使用场景
- 用脚本实现原子业务逻辑(库存扣减、限流、分布式锁)时评估阻塞边界
- 线上出现 BUSY 报错,定位是哪段脚本在长时间运行
- 代码评审:确认脚本里没有全量遍历或大批量循环
- 灰度发布前做压测,确认脚本在目标数据规模下的 P99 耗时
- 事故处置时判断该用 SCRIPT KILL 还是直接停机
超时后的处置
超时后的处置
redis-cli CLIENT LIST | grep cmd=lua # 找到正在执行脚本的连接
redis-cli SCRIPT KILL # 脚本未写数据时可安全终止
redis-cli SCRIPT KILL # 已写数据时会返回 UNKILLABLE
redis-cli SHUTDOWN NOSAVE # 最后手段:不保存退出,再评估重放
redis-cli INFO commandstats | grep -i eval # 平时用调用量与耗时观察脚本开销超时阶段的行为
| 阶段 | 服务器表现 | 可执行操作 |
|---|---|---|
| 未超时 | 正常处理所有命令 | 无 |
| 超过 lua-time-limit | 打日志,其他请求得到 BUSY | 仍可执行 SCRIPT KILL 与 SHUTDOWN NOSAVE |
| 脚本只读未写 | 可安全终止 | SCRIPT KILL 生效 |
| 脚本已写入数据 | 无法终止 | 只能停机,或等脚本自行跑完 |
脚本里最危险的不是单条命令,而是循环:遍历一个几十万元素的集合,或在脚本里做大段字符串拼接与排序,耗时都会随数据规模线性增长。更稳的做法是把大规模计算放到应用侧,脚本里只保留「条件判断加写入」的核心逻辑。