今天無聊,想試試 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 的衝動.............