kill命令的使用技巧(老板不了解kill原理)

前言

在平时使用MySQL的过程中,不知道你是否遇到一个查询很长时间没有出结果,这个时候,你可能会:执行kill query 线程id来终止这个查询语句;或者,kill connectIOn 线程id断开连接;又或者,Ctrl C结束线程终止查询。

但是,是不是会发现一个奇怪的现象,可能你在客户端感觉不到,但是这个时候如果有交易并且涉及刚刚查询的表,可能就会超时,看binlog日志会发现查询并没有结束?这是为什么呢?

其实大多数情况下,kill query/connection 命令是有效的。比如,执行一个查询的过程中,发现执行时间太久,要放弃继续查询,这时我们就可以用 kill query 命令,终止这条查询语句。

还有一种情况是,语句处于锁等待的时候,直接使用 kill 命令也是有效的。如下:

kill命令的使用技巧(老板不了解kill原理)(1)

但是,我们思考一下:session B 是直接终止掉线程,什么都不管就直接退出吗?显然,这是不行的。

线程收到kill指令后做什么呢?

了解一定MySQL的人应该都知道,当对一个表做增删改查操作时,会在表上加 MDL 读锁。上图session B 虽然处于 blocked 状态,但还是拿着一个 MDL 读锁的。如果线程被 kill 的时候,就直接终止,那之后这个 MDL 读锁就没机会被释放了。

由此,我们可以了解到,线程收到kill指令后并不会马上停止,而是会等执行完当前的操作才会停止。

其实,这跟 Linux 的 kill 命令类似,kill -N pid 并不是让进程直接停止,而是给进程发一个信号,然后进程处理这个信号,进入终止逻辑。只是对于 MySQL 的 kill 命令来说,不需要传信号量参数,就只有“停止”这个命令。

kill query thread_id过程

MySQL线程收到kill query thread_id_B指令后,做了两件事:

  1. 把 session B 的运行状态改成 THD::KILL_QUERY(将变量 killed 赋值为 THD::KILL_QUERY);
  2. 给 session B 的执行线程发一个信号。发一个信号的目的,就是让 session B 退出等待,来处理这个 THD::KILL_QUERY 状态。
kill不掉的查询-线程没有执行到判断线程状态
  • 前提

setglobal InnoDB_thread_concurrency=2,将 InnoDB 的并发线程上限数设置为 2;

kill命令的使用技巧(老板不了解kill原理)(2)

可以看到:

  1. sesssion C 执行的时候被堵住了;
  2. 但是 session D 执行的 kill query C 命令却没什么效果,
  3. 直到 session E 执行了 kill connection 命令,才断开了 session C 的连接,提示“Lost connection to MySQL server during query”
  4. 但是这时候,如果在 session E 中执行 show processlist,你就能看到下面这个图。

kill命令的使用技巧(老板不了解kill原理)(3)

在这个例子里,12 号线程的等待逻辑是这样的:每 10 毫秒判断一下是否可以进入 InnoDB 执行,如果不行,就调用 nanosleep 函数进入 sleep 状态。

也就是说,虽然 12 号线程的状态已经被设置成了 KILL_QUERY,但是在这个等待进入 InnoDB 的循环过程中,并没有去判断线程的状态,因此根本不会进入终止逻辑阶段。

那为什么执行 show processlist 的时候,会看到 Command 列显示为 killed 呢?其实,这就是因为在执行 show processlist 的时候,有一个特别的逻辑:

如果一个线程的状态是KILL_CONNECTION,就把Command列显示成Killed。

这个例子是 kill 无效的第一类情况,即:线程没有执行到判断线程状态的逻辑。跟这种情况相同的,还有由于 IO 压力过大,读写 IO 的函数一直无法返回,导致不能及时判断线程的状态。

kill不掉的查询-终止逻辑耗时较长

这类情况常见的几种场景:

  1. 超大事务执行期间被 kill。这时候,回滚操作需要对事务执行期间生成的所有新数据版本做回收操作,耗时很长。
  2. 大查询回滚。如果查询过程中生成了比较大的临时文件,加上此时文件系统压力大,删除临时文件可能需要等待 IO 资源,导致耗时较长。
  3. DDL 命令执行到最后阶段,如果被 kill,需要删除中间过程的临时文件,也可能受 IO 资源影响耗时较久。
Ctrl C

相信大家一定在客户端执行查询时,使用过Ctrl C终止查询吧,在客户端看起来是终止,但是,其实查询并不一定立马终止了的。

其实在客户端的操作只能操作到客户端的线程,客户端和服务端只能通过网络交互,是不可能直接操作服务端线程的。

而由于 MySQL 是停等协议,所以这个线程执行的语句还没有返回的时候,再往这个连接里面继续发命令也是没有用的。

实际上,执行 Ctrl C 的时候,是 MySQL 客户端另外启动一个连接,然后发送一个 kill query 命令。所以,Ctrl C最终还是要执行kill query的流程的。

总结

今天,给大家介绍了一些kill不掉的情况,这些“kill 不掉”的情况,其实是因为发送 kill 命令的客户端,并没有强行停止目标线程的执行,而只是设置了个状态,并唤醒对应的线程。而被 kill 的线程,需要执行到判断状态的“埋点”,才会开始进入终止逻辑阶段。并且,终止逻辑本身也是需要耗费时间的。

所以,如果你发现一个线程处于 Killed 状态,你可以做的事情就是,通过影响系统环境,让这个 Killed 状态尽快结束。

另外,这里还有一点要跟大家强调下的,也是我在实际工作中的了解:

  1. 如果我们要查询一些数据,尽量不要查询主库,因为主库上有很多交易的写操作,尽量在从库执行,影响小一些。
  2. 不要在生产执行复杂的大查询,即使是从库也是会影响到正常交易的读操作的。
  3. 如果真的需要周期性查询一些数据,可以选择从大数据中心的备份数据里读取,大数据中心一般都会同步生产系统的相关表和数据的。或者可以通过程序在非交易时间段(或交易量少的时候)进行查询。

以上,希望有帮助到大家,在客户端执行查询一定要先分析sql是否可以在生产执行呢~可以先在准生产环境先执行也是能防患未然的呢。

(欢迎关注留言~一起学习进步呀)

,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页