script kill 不能直接解决 cpu 100%,仅在脚本未执行写命令且未超时的情况下可中止阻塞,否则返回 busy 错误;其核心价值是作为判断脚本状态与实施干预的关键入口。

SCRIPT KILL 能否解决 CPU 100%?不能直接解决,但它是判断和干预的关键入口——前提是脚本还没执行写命令。
为什么 SCRIPT KILL 在 CPU 100% 时经常“没反应”
Redis 单线程模型下,一个陷入死循环的 Lua 脚本(如 while true do end)会彻底霸占主线程,导致所有命令排队、redis-cli monitor 滞后、甚至 INFO 统计都卡住。此时你执行 SCRIPT KILL,常见现象是命令卡住或返回 (error) BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.。
这不是命令失效,而是脚本已进入「不可中断」状态。根本原因只有两个:
- 脚本已调用过
redis.call("SET", ...)、HSET等任意写操作 —— 此时 Redis 为保数据一致性,强制拒绝中断 - 脚本虽未写,但已超
lua-time-limit(默认 5000ms),被标记为 BUSY,而你连接的不是真正执行该脚本的节点(集群模式下尤其常见)
如何确认当前是否有可 kill 的阻塞脚本
别急着输 SCRIPT KILL,先验证状态。在疑似异常的 Redis 实例上执行:
redis-cli INFO | grep -E "(busy|lua|used_memory_rss_human)"
重点关注三项:
-
lua_script_busy:1表示有脚本正在运行(但不说明是否可 kill) -
used_memory_rss_human是否持续上涨?若稳定在某值,说明脚本纯计算无内存分配;若暴涨,可能在本地构造大 table 或反复调用redis.call -
cmdstat_eval:calls=xxx,usec_per_call=yyyyy中usec_per_call若远高于 5000000(5秒)且无回落,基本确认卡死
再补查一次只读性:
redis-cli --eval /dev/stdin 0 <p>如果这个简单只读脚本能立即返回,说明实例响应通道还通;如果也卡住,大概率已写入,<code>SCRIPT KILL</code> 无效。</p><div class="aritcle_card flexRow artxards"> <div class="artcardd flexRow"> <a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill3464" title="Redis Skill - 高性能缓存管理"><img src="https://img.php.cn/upload/skill/000/000/081/178960683454849.jpg" alt="Redis Skill - 高性能缓存管理" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a> <div class="aritcle_card_info flexColumn"> <a rel="nofollow" href="/xiazai/skill3464" title="Redis Skill - 高性能缓存管理" class="overflowclass">Redis Skill - 高性能缓存管理</a> <p class="overflowclass">Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。</p> </div> <a rel="nofollow" href="/xiazai/skill3464" title="Redis Skill - 高性能缓存管理" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a> </div> </div> <h3>SCRIPT KILL 的正确执行姿势</h3> <p>必须满足三个条件才能成功返回 <code>OK</code>:</p>
- 连接到**实际运行该脚本的 Redis 节点**:集群模式下,用
CLUSTER KEYSLOT <key></key>确认分片,再直连对应 node;非集群则直连主实例 - 脚本全程未触发任何写命令:包括
SET、HSET、LPUSH、DEL,甚至EXPIRE—— 只允许GET、EXISTS、KEYS(慎用)、redis.pcall("GET",...) - 执行顺序:先查状态,再连同一连接,立刻发
SCRIPT KILL(不带空格、不加引号、不跟参数)
示例流程:
redis-cli -h 10.0.1.12 -p 6379 INFO | grep lua_script_busy<br>redis-cli -h 10.0.1.12 -p 6379 SCRIPT KILL
返回 OK 表示成功;返回 (error) NO SCRIPT 表示当前无运行脚本;返回 BUSY 错误,则写操作已发生,SCRIPT KILL 失效。
当 SCRIPT KILL 失效时,唯一安全的兜底动作
一旦确认脚本已写入(BUSY 错误返回),就不要再试 SCRIPT KILL。此时继续等待或盲目重启风险极高:
- 等它自然结束?可能几小时,期间所有请求失败
- 用
SHUTDOWN NOSAVE?能立即退出,但会丢失自上次 RDB/AOF 后所有写入 —— 生产环境需评估数据容忍度 - 改配置热重载?
CONFIG SET lua-time-limit 1000对已运行脚本无效
真正可行的路径只有一条:在脚本写入前就预防。上线前必须做三件事:
- 所有循环必须带显式计数器:
for i = 1, 1000 do ... if i >= 1000 then break end end - 禁用裸
KEYS,改用SCAN+ 客户端过滤;避免在循环内反复redis.call("GET", key) - 启动参数强制设
--lua-time-limit 2000,比默认 5000 更早触发 BUSY 标记,留给SCRIPT KILL更多窗口
最易被忽略的一点:集群环境下,SCRIPT KILL 必须打到具体分片,而不是入口代理。很多团队卡在这一步,反复在 proxy 上执行却毫无效果。










