“BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE” 说明一个 Lua 脚本(或函数)占住了 Redis 的主线程。这篇在本机用死循环和慢脚本复现:其它客户端什么时候开始收到 BUSY、BUSY 期间到底什么还能用、SCRIPT KILL 什么时候杀不掉、阈值设大设小各有什么后果。
busy-reply-threshold = 1000 ms(默认值是 5000 ms,这里改成 1000 ms 省得等);lua-time-limit(旧名字)= 1000 死循环脚本启动 0.2 s 之后,另一个连接发 GET k -> (error) BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.,等了 0.80 s 紧接着再发一次 GET k -> (error) BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.,用了 1.2 ms(阈值已过,立刻回 BUSY) SCRIPT KILL -> "OK" 死循环那个连接最后收到: (error) ERR Script killed by user with SCRIPT KILL... script: 694a5fe1ddb97a4c6a1bf299d9537c7d3d0f84e7, on @user_script:1. 之后 GET k -> "v"
阈值之前,其它客户端的命令没有任何回复,到了阈值才开始回 BUSY。SCRIPT KILL 能杀没写过数据的脚本。
校准:20000000 次加法的 Lua 循环用了 0.19 s,所以 104357739 次约 1 秒 busy-reply-threshold = 3000 ms,脚本是约 1 秒的计算循环:另一个连接 GET k -> "v",等了 1.70 s;脚本本身返回 "done",共 1.91 s busy-reply-threshold = 300 ms,脚本是约 1 秒的计算循环:另一个连接 GET k -> (error) BUSY Redis is busy running a script. You can only ca,等了 0.10 s;脚本本身返回 "done",共 1.06 s
阈值比脚本长,别人只是被挂住;比脚本短,别人收到 BUSY 报错,脚本都会跑完。
BUSY 期间 PING -> BUSY BUSY 期间 GET k -> BUSY BUSY 期间 INFO server -> BUSY BUSY 期间 CLIENT LIST -> BUSY BUSY 期间 CONFIG GET maxmemory -> BUSY BUSY 期间 SET x 1 -> BUSY BUSY 期间 SCRIPT FLUSH -> BUSY BUSY 期间 FUNCTION LIST -> BUSY BUSY 期间 CONFIG SET busy-reply-threshold 5000 -> BUSY SCRIPT KILL -> "OK" 脚本被杀掉之后再 SCRIPT KILL -> (error) NOTBUSY No scripts in execution right now.
几乎什么都做不了,包括 INFO 和改配置。只能用 SCRIPT KILL / FUNCTION KILL / SHUTDOWN NOSAVE。
先写后死循环的脚本:BUSY 期间 SCRIPT KILL -> (error) UNKILLABLE Sorry the script already executed write commands against the dataset. You can either wait the script termination or kill the server in a hard way using the SHUTDOWN NOSAVE command. 发 SHUTDOWN NOSAVE 之后,服务器进程还在吗: False;死循环那个连接收到: (连接被关闭,没有应答) 重新起一个实例:GET written -> (nil)(没有持久化,SHUTDOWN NOSAVE 之后脚本里已经写过的数据也不在了)
脚本一旦写过数据,SCRIPT KILL 返回 UNKILLABLE,只能等它结束或 SHUTDOWN NOSAVE,没持久化的数据会一起丢。
BUSY 期间 GET k -> (error) BUSY Redis is busy running a script. You can only call FUNCTION KILL or SHUTDOWN NOSAVE. SCRIPT KILL -> (error) BUSY Redis is busy running a script. You can only call FUNCTION KILL or SHUTDOWN NOSAVE. FUNCTION KILL -> "OK" 死循环那个 FCALL 最后收到: (error) ERR Script killed by user with SCRIPT KILL... script: forever, on @user_function:2. 之后 GET k -> (nil),FUNCTION KILL 再来一次 -> (error) NOTBUSY No scripts in execution right now.
FCALL 卡住时要用 FUNCTION KILL,SCRIPT KILL 不行,提示里会写明该用哪个。