跑一个纯CPU计算的Lua脚本,同时从另一个连接发PING,验证Redis单线程模型下脚本执行期间其它客户端经历的是静默等待还是明确报错,以及5秒阈值的意义。
上一篇看到Lua脚本本身省了网络round-trip很快,这篇看它慢的时候有多"霸道"。
代码script = """ local x = 0 for i=1,500000000 do x = x + 1 end return x """ 线程A: 发eval(script) 主线程: 0.1秒后,从另一个连接发PING
这才是真正违反直觉的地方——不是变慢,是完全没反应。
结果busy_time (脚本总耗时): 2.316365957260132 ping_wait (PING等了多久才收到响应): 2.219856023788452 ping_result: PONG normally (blocked silently, no error)
验证:PING从发出到收到PONG,等了2.2秒——几乎等于脚本本身的总耗时。这不是网络延迟,也不是超时重试,是Redis单线程模型最朴素的体现:脚本在执行的这段时间里,Redis的主线程完全被占用着,连读取PING这条命令本身的网络数据这件事都没有机会去做,PING命令是在脚本执行完、主线程腾出手来之后,才被读取、执行、回复的。
Redis有一个叫lua-time-limit的配置,本机默认值是5000毫秒。
config getlua-time-limit: 5000 结果(一个跑超过5秒的脚本): ping_wait: 4.900055170059204 ping_result: error: BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.
-BUSY错误,明确告诉你"我正忙着跑一个脚本,你现在只能发SCRIPT KILL或者SHUTDOWN NOSAVE",脚本本身则继续在后台跑,不会自动停。的设计意图很清楚:Redis单线程没有办法真的"暂停"一个正在执行的Lua脚本去处理别的请求,5秒阈值只是给了运维一个信号——"这个脚本可能失控了,你可以考虑手动介入",而不是真的解除了阻塞。
每一步都是本文上面跑出来的原始结果。