TiDB 源碼閱讀(二.1)TiDB 中 的 Kill Query

今天無聊,想試試 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

那是什麼參數控制的呢?翻一下官方文檔:

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 的衝動.............

相關文章
相關標籤/搜索