紧急处理方法中介绍了通过kill杀掉空闲连接来解决短连接风暴的问题,不过除了kill connection来关闭连接之外,还有kill query + 线程id来终止指定线程正在执行的sql。

但是在实际操作中,会存在使用了kill命令,但是却没有断开连接,使用show processlist会看到这条语句的Command的列显示Killed。

要想知道原因,首先就要知道kill是如何处理的。

收到kill的线程如何处理

一个语句在执行的过程中,会获取各种锁资源,例如查询就要先获取MDL读锁,kill肯定不是直接退出,否则这些锁资源没有释放,其它线程也就无法操作对应的数据了。

所以一个线程收到kill,实际上是说可以开始执行停止的逻辑了。

当用户执行 kill query thread_id_B 时,MySQL 里处理 kill 命令的线程做了两件事:

  1. 把 session B 的运行状态改成 THD::KILL_QUERY(将变量 killed 赋值为 THD::KILL_QUERY);
  2. 给 session B 的执行线程发一个信号。

这样就将session B的状态进行更改,并由session B进入到终止逻辑。

之所以出现无法kill的语句,就是在如何判断一个数据库是否出现问题中提到的innodb_thread_concurrency并发数不够用的情况。

我们要kill掉的执行语句,其运行状态已经变成了THD::KILL_QUERY,但是因为并发执行数不够,所以一直在等待进入innodb,没有去判断线程的状态,也就不会进入到终止逻辑退出

对于大事务,长查询它们的终止逻辑执行比较长,所以也会出现这种情况。

关于客户端的两个误解

当一个库中的表很多的时候,客户端连接到mysql就会很慢。 但是这并不是连接慢,因为建立连接就是tcp握手、用户校验、获取权限,与表的数量并没有关系。

但是在使用默认参数建立连接的时候,客户端会提供一个本地库名和表名的自动补全功能,所以在建立连接之后,客户端会执行下面的逻辑:

  1. 执行show databases;
  2. 执行show tables;
  3. 将上面两个命令的结果构建一个本地的哈希表。

所以在第三步的时候,如果表很多,那么就会影响哈希表构建的速度,所以感知到的连接慢其实是客户端本地慢

可以使用-A参数不使用自动补全功能。

—quick参数,这个并不是让服务器加速,而是让客户端加速的,有以下作用:

  • 跳过自动补全功能
  • 不使用本地缓存来接收服务端的数据,避免查询结果过大,影响本地的性能,但是可能会阻塞服务端的发送,影响服务端的性能。
  • 不把执行命令记录到本地的命令历史文件中。