共计 4123 个字符,预计需要花费 11 分钟才能阅读完成。
明天无聊,想试试 kill query,次要是想 debug 一下 jdbc 8 的 kill query 逻辑。不试不晓得,一试就狐疑人生。
起初验证一下,看看怎么做的
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 掉。卡在那里。不过有个 warning
mysql> select sleep (1000);
于是我试了下 MySQL 的逻辑,
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)
另一边
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 的行为,去除一些其余的货色就是如上所示
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
那是什么参数管制的呢?翻一下官网文档:
compatible-kill-query
设置 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 的激动 ………….