TiDB 源码阅读(二.1)TiDB 中 的 Kill Query

Sev7ndayYo · · 529 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

今天无聊,想试试 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 的冲动.............


有疑问加站长微信联系(非本文作者)

本文来自:Segmentfault

感谢作者:Sev7ndayYo

查看原文:TiDB 源码阅读(二.1)TiDB 中 的 Kill Query

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

529 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传