MySQL 执行Kill命令后,执行命令为啥没马上停止?
这些“kill 不掉”的情况,其实是因为发送 kill 命令的客户端,并没有强行停止目标线程的执行,而只是设置了个状态,并唤醒对应的线程。而被 kill 的线程,需要执行到判断状态的“埋点”,才会开始进入终止逻辑阶段。并且,终止逻辑本身也是需要耗费时间的。
kill 命令
MySQL 提供了 kill query + 线程 id 和 kill [connection] + 线程id 两种停止执行命令的指令。
kill connection 中的 connection 可以省略。
kill connection 命令执行过程:
1、把线程状态设置为KILL_CONNECTION
2、关闭请求线程的网络连接。此时show processlist的显示结果为Killed。主要原因是如果一个线程的状态是KILL_CONNECTION, 就把Command列显示成Killed。
kill connection 先把客户端的sql连接断开,后续执行流程走kill query
kill 命令的运行原理
将执行线程的运行状态改成THD::KILL_QUERY状态,并向执行线程发一个信号,这个信号有点像 我们用kill -9 向操作系统发送终止信号。
另外kill connection 会断开网络连接,然后客户端会重新在一个线程发送kill query命令,show processlist会将kill connection的状态显示为Killed。然而其实innodb 可能因为io繁忙,锁等待,并发线程数不够,而没有机会终止当前线程。出现这种情况,及时腾出系统资源,比如IO,终止掉其他线程,增大innodb_thread_concurrency, 然后等待线程执行完毕。
总而言之可以分成两大类:
- 线程没有执行到判断线程状态的逻辑
- 终止逻辑耗时太长:
- kill超大事务,回滚时需要回收事务期间生成的数据包版本,耗时很长;
- DDL命令执行到最后阶段,如果被kill, 需要删除中间过程的临时文件,也可能受IO资源影响耗时较久。
- 大查询回滚,如果查询过程中生成了比较大的临时文件,加上此时文件系统压力大,删除临时文件可能需要等待IO资源,导致耗时较长。
这也是碰到一个被Killed事务一直处于回滚状态时,此时重启MySQL线程也没用,重启后事务还是会回滚。
其他
如果数据集的表很多,比如6万多表,客户端连接上去时,需要初始化本地资源,用来支持命令行不全表名,这个时候可以通过 -A 参数略过。
另外--quick 可以达跳过自动补全功能,quick指的时加速客户端的目的,此时客户端放弃为请求结果使用本地缓存的策略,而是不缓存,读一个处理一个,这样反而会影响服务端的吞吐量。