脚本缓存管理是什么
Redis 执行 Lua 脚本时会先把脚本内容缓存起来,并返回其 SHA1 摘要:此后客户端只需发送 40 字节的摘要调用(EVALSHA),不必每次带上完整脚本体,省带宽也省解析时间。要记住两点:脚本缓存是内存里的、重启或 SCRIPT FLUSH 后清空;缓存中没有摘要时调用会返回 NOSCRIPT 错误,客户端必须能自动回退到 EVAL 并重新加载。
基本信息
| 适用版本 | Redis 2.6+ 支持 Lua 脚本,7.0 起默认使用 Lua 5.1(保留 EVAL 语义) |
|---|---|
| 加载 | SCRIPT LOAD <script> 返回 SHA1 摘要 |
| 调用 | EVALSHA <sha1> <numkeys> <key...> <arg...> |
| 存在性检查 | SCRIPT EXISTS <sha1>... |
| 清理 | SCRIPT FLUSH [ASYNC|SYNC],SCRIPT KILL 终止超时脚本 |
| 错误码 | NOSCRIPT 表示缓存中没有该脚本 |
典型使用场景
- 高频原子操作:把查改写成脚本减少往返,配合 EVALSHA 节省带宽
- 限流与计数:用脚本保证检查与自增的原子性
- 客户端脚本管理:首次 EVAL,后续 EVALSHA,遇 NOSCRIPT 自动重载
- 版本变更验证:用 SHA1 确认部署的脚本版本正确
- 故障排查:实例重启后相关功能全部报错,通常就是脚本缓存丢失
加载与调用
加载与调用
SCRIPT LOAD "return redis.call('GET', KEYS[1])"
# 返回 SHA1 摘要,例如:"4e6d8fc8f0f3f0d4..."
# 用摘要调用:只发 40 字节,不用带脚本体
EVALSHA 4e6d8fc8f0f3f0d4... 1 mykey
SCRIPT EXISTS 4e6d8fc8f0f3f0d4... # 1 表示在缓存中,0 表示不在
SCRIPT FLUSH ASYNC # 清空脚本缓存(异步执行避免阻塞)
SCRIPT KILL # 终止执行超时的脚本(仅限未写入的脚本)命令与风险
| 命令 | 作用 | 注意 |
|---|---|---|
| SCRIPT LOAD | 加载脚本并返回 SHA1 | 同一脚本内容摘要固定,可预加载 |
| EVALSHA | 按摘要执行 | 缓存缺失时报 NOSCRIPT |
| SCRIPT EXISTS | 检查摘要是否存在 | 批量检查时返回 0/1 数组 |
| SCRIPT FLUSH | 清空所有脚本缓存 | 异步方式可减少阻塞 |
| SCRIPT KILL | 终止正在执行的脚本 | 脚本已执行写命令时无法终止 |
主流客户端(redis-py、Jedis、Lettuce 等)都内置脚本管理:首次用 EVAL 执行并把脚本与摘要对应缓存到本地,之后用 EVALSHA,遇到 NOSCRIPT 自动回退重试;自研客户端需要自己实现这套逻辑。
脚本里执行过写命令后又超时,SCRIPT KILL 无法终止它:实例会一直阻塞到脚本自然结束或触发 busy-reply 阈值,因此脚本必须短小、避免遍历大 key,并在客户端设置合理的超时与降级策略。