今天无聊,想试试 kill query,主要是想 debug 一下 jdbc 8 的 kill query 逻辑。不试不知道,一试就怀疑人生。html
后来验证一下,看看怎么作的mysql
MySQL [(none)]> show processlist; +------+------+-----------+------+---------+------+-------+--------------------+ | Id | User | Host | db | Command | Time | State | Info | +------+------+-----------+------+---------+------+-------+--------------------+ | 22 | root | 127.0.0.1 | NULL | Query | 0 | 2 | show processlist | | 23 | root | 127.0.0.1 | NULL | Query | 10 | 2 | select sleep(1000) | | 17 | root | 127.0.0.1 | NULL | Sleep | 1 | 2 | NULL | +------+------+-----------+------+---------+------+-------+--------------------+ 3 rows in set (0.00 sec) MySQL [(none)]> kill query 23; Query OK, 0 rows affected, 1 warning (0.00 sec)
如上,结果你们也能想到,select sleep(1000) 没有被 kill 掉。卡在那里。不过有个 warninggit
mysql> select sleep (1000);
因而我试了下 MySQL 的逻辑,github
mysql> show processlist; +----+-----------------+-----------+------+---------+--------+------------------------+---------------------+ | Id | User | Host | db | Command | Time | State | Info | +----+-----------------+-----------+------+---------+--------+------------------------+---------------------+ | 5 | event_scheduler | localhost | NULL | Daemon | 177856 | Waiting on empty queue | NULL | | 23 | root | localhost | NULL | Query | 0 | starting | show processlist | | 24 | root | localhost | NULL | Query | 6 | User sleep | select sleep (6000) | +----+-----------------+-----------+------+---------+--------+------------------------+---------------------+ 3 rows in set (0.00 sec) mysql> kill query 24; Query OK, 0 rows affected (0.00 sec)
另外一边sql
mysql> select sleep (6000); +--------------+ | sleep (6000) | +--------------+ | 1 | +--------------+ 1 row in set (17.09 sec)
看来是生效了。为何会这样?服务器
因而,我火烧眉毛的去看了下 kill query 的执行逻辑。负载均衡
直接在 parser.y 中搜 kill 吧,一共匹配 26 个,找到 Kill Statement :命令行
/******************************************************************** * Kill Statement * See https://dev.mysql.com/doc/refman/5.7/en/kill.html *******************************************************************/ KillStmt: | KillOrKillTiDB "QUERY" NUM { $$ = &ast.KillStmt{ ConnectionID: getUint64FromNUM($3), Query: true, TiDBExtension: $1.(bool), } }
既然咱们要查的是 kill query 的行为,去除一些其余的东西就是如上所示debug
KillOrKillTiDB "QUERY" NUM
这里两个 token ,KillOrKillTiDB 和 NUM,NUM不用说了带面的是数字,即 connection id 。设计
KillOrKillTiDB 是什么?
KillOrKillTiDB: "KILL" { $$ = false } /* KILL TIDB is a special grammar extension in TiDB, it can be used only when the client connect to TiDB directly, not proxied under LVS. */ | "KILL" "TIDB" { $$ = true }
接着看下 executeKillStmt 的 实现
func (e *SimpleExec) executeKillStmt(s *ast.KillStmt) error { conf := config.GetGlobalConfig() if s.TiDBExtension || conf.CompatibleKillQuery { sm := e.ctx.GetSessionManager() if sm == nil { return nil } sm.Kill(s.ConnectionID, s.Query) } else { err := errors.New("Invalid operation. Please use 'KILL TIDB [CONNECTION | QUERY] connectionID' instead") e.ctx.GetSessionVars().StmtCtx.AppendWarning(err) } return nil }
先读取配置,若是符合配置才能执行 sm.Kill(s.ConnectionID, s.Query) 操做。
否则直接报错
Invalid operation. Please use 'KILL TIDB [CONNECTION | QUERY] connectionID' instead
那是什么参数控制的呢?翻一下官方文档:
设置 KILL 语句的兼容性。
默认值:false
TiDB 中 KILL xxx 的行为和 MySQL 中的行为不相同。为杀死一条查询,在 TiDB 里须要加上 TIDB 关键词,即 KILL TIDB xxx。但若是把 compatible-kill-query 设置为 true,则不须要加上 TIDB 关键词。
这种区别很重要,由于当用户按下 Ctrl+C 时,MySQL 命令行客户端的默认行为是:建立与后台的新链接,并在该新链接中执行 KILL 语句。若是负载均衡器或代理已将该新链接发送到与原始会话不一样的 TiDB 服务器实例,则该错误会话可能被终止,从而致使使用 TiDB 集群的业务中断。只有当您肯定在 KILL 语句中引用的链接正好位于 KILL 语句发送到的服务器上时,才能够启用 compatible-kill-query。
即须要是 kill tidb query 才能生效。
对于这个设计,emm,以前也遇到过相似的坑,客户端发 kill query ,但由于 f5是优先发负载低的 dbproxy ,因此若是不幸被选中的 dbproxy 上也有一样的 connection id ,就会发生比较严重的问题。不知道这个设计是否是也出于相似的目的。不过这种一刀切,应用不可能让 kill query 不生效的吧?
先无论那些。
我们先再去试一下 kill tidb 的语法
MySQL [(none)]> show processlist; +------+------+-----------+------+---------+------+-------+--------------------+ | Id | User | Host | db | Command | Time | State | Info | +------+------+-----------+------+---------+------+-------+--------------------+ | 17 | root | 127.0.0.1 | NULL | Sleep | 2 | 2 | NULL | | 25 | root | 127.0.0.1 | NULL | Query | 0 | 2 | show processlist | | 27 | root | 127.0.0.1 | NULL | Query | 4 | 2 | select sleep(6000) | +------+------+-----------+------+---------+------+-------+--------------------+ 3 rows in set (0.00 sec) MySQL [(none)]> kill tidb query 27; Query OK, 0 rows affected (0.00 sec)
同样的操做,结果如何呢
MySQL [(none)]> select sleep(6000); +-------------+ | sleep(6000) | +-------------+ | 1 | +-------------+ 1 row in set (13.43 sec) MySQL [(none)]>
嗯,是生效了。
不过一个疑问出来了,为何单纯执行 kill query 显示的是执行成功而没有代码中的那个报警提示
Invalid operation. Please use 'KILL TIDB [CONNECTION | QUERY] connectionID' instead
tidb 的 log 中也没有,是否是 bug ?(黑人问号脸),有去 github 提 issue 的冲动.............