必须用 scan 替代 keys,分批执行 del,并合理设置 lua-time-limit;生产环境需禁用 keys 命令。scan 分批迭代、避免阻塞;del 每批不超过 500 个 key;lua-time-limit 应调至足够宽松并加脚本内超时防护;keys 必须通过 rename-command 彻底禁用。

SCAN 代替 KEYS 是硬性前提
直接在 Lua 脚本里调用 KEYS,哪怕只写 redis.call("keys", "user:*"),也会触发全库扫描、锁住主线程。Redis 单线程模型下,这等于让所有请求排队等它扫完——几万个 key 就可能卡住 1–2 秒,监控上 redis_blocked_clients 会陡增,redis_latency_ms 持续超 100ms。
必须改用 SCAN:它基于游标分批返回,每次只拿一部分 key(比如 COUNT 1000),不阻塞服务。Lua 脚本里不能只调一次 SCAN 就完事,得循环迭代直到游标返回 0。
-
SCAN返回的是{cursor, {key1,key2,...}}结构,脚本里需解构游标和 key 列表 - 集群模式下
SCAN只作用于当前 slot,若前缀跨 slot,需客户端协调多个节点(Lua 本身无法跨 slot 执行) - 别信“
EVAL "return redis.call('keys', ...)"看似一条命令就安全”——内部仍是阻塞式KEYS,本质没变
DEL 必须分小批次执行
即使 SCAN 拿到了一批 key,也不能一股脑传给 DEL。Lua 的 unpack() 对参数数量有限制,匹配出 50 万个 key 时,unpack(keys) 很可能触发栈溢出或超时;Redis 本身对单次命令参数个数也有隐式限制(通常 16K 左右)。
推荐每批最多删 500 个 key,用循环切片方式调用 DEL:
local batch_size = 500
for i = 1, #keys, batch_size do
local batch = {}
for j = i, math.min(i + batch_size - 1, #keys) do
table.insert(batch, keys[j])
end
redis.call("del", unpack(batch))
end
- 避免用
redis.pcall("del", ...)包裹——除非你明确要吞掉错误;正常场景用redis.call更利于暴露问题 - 不要在脚本里加 sleep 或重试逻辑,那是客户端该干的事;Lua 只负责“这次删得准不准”
- 如果某次
DEL失败(比如 key 不存在),redis.call会直接报错中断脚本,需确保上游能捕获并处理
lua-time-limit 必须设合理值
Redis 默认 lua-time-limit 是 5000ms(5 秒),但海量 key 场景下,一次完整 SCAN+DEL 循环很容易超时。超时后 Redis 会强制中断脚本,但已执行的 DEL 不会回滚——结果就是删了一半停了,下次还得重来,还可能漏删或重复删。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
建议根据数据规模预估时间,把配置调到足够宽松(比如 30000ms),同时在脚本开头加超时防护:
local start_time = redis.call("time")[1]
-- 后续循环中检查:if redis.call("time")[1] - start_time > 25 then return "timeout" end
- 别依赖客户端设置 timeout 参数(如
eval ... <timeout></timeout>),Redis 不支持这个语法;超时控制只能靠配置项或脚本内手动判断 - 升级到 Redis 8.2.3 后,CVE-2025-62507 已修复,但
lua-time-limit仍需人工确认是否生效(某些容器化部署会覆盖配置) - 脚本里调用
redis.time()比redis.call("time")更轻量,但精度略低;高精度场景选后者
生产环境必须禁用 KEYS 命令
光靠“教育开发别用 KEYS”不靠谱。线上应直接通过 rename-command KEYS "" 在 redis.conf 中禁掉,让任何尝试执行 KEYS 的请求都返回错误。这是兜底手段,比靠人盯代码或 CI 检查更可靠。
禁用后,所有依赖 KEYS 的旧脚本、运维命令、监控脚本都会立即失败,正好倒逼团队切换到 SCAN 方案。
- 禁用后,
redis-cli KEYS *会报(error) ERR unknown command `KEYS`,不是静默忽略 - 如果用了 Redis Proxy(如 Twemproxy、Codis),需确认 proxy 层是否透传 rename 配置;部分 proxy 会拦截并报错
- 某些 APM 工具(如 SkyWalking)自动采集
KEYS调用,禁用后可能触发告警,需同步调整规则
真正麻烦的不是写对脚本,而是确保它跑在正确的 Redis 版本、配置和拓扑下——集群分片、proxy 中间件、连接池复用、客户端超时设置,任何一个环节没对齐,都可能让看似完美的 Lua 脚本在线上失效。










