當mysql 遇到 ctrl+c


目的

       為了理解MySQL在執行大SQL時,對執行CTRL+C產生的疑惑,本文通過實驗測試和源碼分析兩個方面,對MySQL處理CTRL+C的詳細過程進行分析和講解,從而解除DBA及開發人員對CTRL+C的誤解。

測試

       首先,基於線上數據庫版本,分別使用MySQL客戶端版本5.5.20和5.0.77進行實驗測試,一方面排除不同數據庫客戶端版本造成的差異,另一方面,深入了解不同版本執行CTRL+C產生的差異。

MySQL客戶端5.5.20

       使用MySQL客戶端5.5.20在Session1中執行select sleep(100)語句,在Session2中執行show processlist語句;然后在Session1中執行CTRL+C,在Session中執行show processlist語句,查看當前連接的線程。執行的圖如下所示:

 

Session1

mysql> select sleep(100);

Ctrl-C -- sending "KILL QUERY 153779" to server ...

Ctrl-C -- query aborted.

ERROR 2013 (HY000): Lost connection to MySQL server during query

 

Session2:

 

 

mysql> show processlist;
+--------+-------------+-----------------+------+-------------+---------+-----------------------------------------------------------------------------+-------------------+-----------+---------------+-----------+
| Id     | User        | Host            | db   | Command     | Time    | State                                                                       | Info              | Rows_sent | Rows_examined | Rows_read |
+--------+-------------+-----------------+------+-------------+---------+-----------------------------------------------------------------------------+-------------------+-----------+---------------+-----------+
| 153779 | heng.wang   | 127.0.0.1:39882 | NULL | Query       |       8 | User sleep                                                                  | select sleep(100) |         0 |             0 |         1 |
| 153780 | heng.wang   | 127.0.0.1:39883 | NULL | Query       |       0 | NULL                                                                        | show processlist  |         0 |             0 |         1 |
+--------+-------------+-----------------+------+-------------+---------+-----------------------------------------------------------------------------+-------------------+-----------+---------------+-----------+
2 rows in set (0.00 sec)

mysql> show processlist;
+--------+-------------+-----------------+------+-------------+---------+-----------------------------------------------------------------------------+------------------+-----------+---------------+-----------+
| Id     | User        | Host            | db   | Command     | Time    | State                                                                       | Info             | Rows_sent | Rows_examined | Rows_read |
+--------+-------------+-----------------+------+-------------+---------+-----------------------------------------------------------------------------+------------------+-----------+---------------+-----------+
| 153780 | heng.wang   | 127.0.0.1:39883 | NULL | Query       |       0 | NULL                                                                        | show processlist |         0 |             0 |         1 |
+--------+-------------+-----------------+------+-------------+---------+-----------------------------------------------------------------------------+------------------+-----------+---------------+-----------+
1 rows in set (0.00 sec)

 

       從以上結果來看,Session1中執行select操作時,Session2中可以查看該連接正在執行,在Session1中執行CTRL+C時,客戶端向服務器端發送KILL QUERY 命令,並且連接關閉。在Session2中可以看到執行select的連接已經關閉。

 

MySQL客戶端5.0.77

       使用客戶端5.0.77執行同樣的操作,執行CTRL+C后,觀察執行的差異性。具體如下表中所示:

 

Session1

mysql> select sleep(100);

Query aborted by Ctrl+C

+------------+

| sleep(100) |

+------------+

|          1 |

+------------+

 

Session2

 

 

mysql> show processlist;
+--------+-------------+-----------------+------+-------------+---------+-----------------------------------------------------------------------------+-------------------+-----------+---------------+-----------+
| Id     | User        | Host            | db   | Command     | Time    | State                                                                       | Info              | Rows_sent | Rows_examined | Rows_read |
+--------+-------------+-----------------+------+-------------+---------+-----------------------------------------------------------------------------+-------------------+-----------+---------------+-----------+
| 153783 | heng.wang   | 127.0.0.1:45807 | NULL | Query       |       3 | User sleep                                                                  | select sleep(100) |         0 |             0 |         1 | 
| 153784 | heng.wang   | 127.0.0.1:45809 | NULL | Query       |       0 | NULL                                                                        | show processlist  |         0 |             0 |         1 | 
+--------+-------------+-----------------+------+-------------+---------+-----------------------------------------------------------------------------+-------------------+-----------+---------------+-----------+
2 rows in set (0.00 sec)

mysql> show processlist;
+--------+-------------+-----------------+------+-------------+---------+-----------------------------------------------------------------------------+------------------+-----------+---------------+-----------+
| Id     | User        | Host            | db   | Command     | Time    | State                                                                       | Info             | Rows_sent | Rows_examined | Rows_read |
+--------+-------------+-----------------+------+-------------+---------+-----------------------------------------------------------------------------+------------------+-----------+---------------+-----------+
| 153783 | heng.wang   | 127.0.0.1:45807 | NULL | Sleep       |      10 |                                                                             | NULL             |         1 |             0 |         1 | 
| 153784 | heng.wang   | 127.0.0.1:45809 | NULL | Query       |       0 | NULL                                                                        | show processlist |         0 |             0 |         1 | 
+--------+-------------+-----------------+------+-------------+---------+-----------------------------------------------------------------------------+------------------+-----------+---------------+-----------+
2 rows in set (0.00 sec)

 

      從以上結果可知,Session1上執行select時,Session2中建立查詢連接;在Session1中執行CTRL+C時,顯示Query被終止,並且返回執行的錯誤結果。在Session2中可知,連接的線程仍然存在,但是Query被終止,只是保持連接。

源碼分析

       為了更進一步對以上測試進行確認,查看MySQL源碼進行進一步的求證,同樣基於MySQL的5.5.20和5.0.77兩個版本進行驗證。具體如下:

MySQL 5.5.20源碼

       MySQL客戶端主函數main中,信號函數在源碼文件client/mysql.cc:1163,源碼如下所示:

 

  signal(SIGINT, handle_sigint);  // Catch SIGINT to clean up

 

       信號處理函數handle_sigint在client/mysql.cc:1291,源碼如下所示:

 

sig_handler handle_sigint(int sig)

{

  char kill_buffer[40];

  MYSQL *kill_mysql= NULL;

  /* terminate if no query being executed, or we already tried interrupting */

  if (!executing_query || (interrupted_query == 2))

  {

    tee_fprintf(stdout, "Ctrl-C -- exit!\n");

    goto err;

  }

  kill_mysql= mysql_init(kill_mysql);

  if (!mysql_real_connect(kill_mysql,current_host, current_user, opt_password,

                          "", opt_mysql_port, opt_mysql_unix_port,0))

  {

    tee_fprintf(stdout, "Ctrl-C -- sorry, cannot connect to server to kill query, giving up ...\n");

    goto err;

  }

  interrupted_query++;

  /* mysqld < 5 does not understand KILL QUERY, skip to KILL CONNECTION */

  if ((interrupted_query == 1) && (mysql_get_server_version(&mysql) < 50000))

    interrupted_query= 2;

  /* kill_buffer is always big enough because max length of %lu is 15 */

  sprintf(kill_buffer, "KILL %s%lu",

          (interrupted_query == 1) ? "QUERY " : "",

          mysql_thread_id(&mysql));

  tee_fprintf(stdout, "Ctrl-C -- sending \"%s\" to server ...\n", kill_buffer);

  mysql_real_query(kill_mysql, kill_buffer, (uint) strlen(kill_buffer));

  mysql_close(kill_mysql);

  tee_fprintf(stdout, "Ctrl-C -- query aborted.\n");

  return;

err:

#ifdef _WIN32

  mysql_thread_end();

  return;

#else

  mysql_end(sig);

#endif 

}

 

       從以上源碼可知,MySQL客戶端在捕獲信號后,將KILL QUERY 命令發送到服務器端執行,從而將Query處理KILL。

MySQL 5.0.77源碼

       MySQL客戶端主函數main中,信號函數在源碼文件client/mysql.cc:1150。源碼如下所示:

 

  signal(SIGINT, mysql_sigint);  // Catch SIGINT to clean up

 

       信號處理函數mysql_sigint在源碼文件client/mysql.cc:1214,源碼如下所示:

 

sig_handler mysql_sigint(int sig)

{

  char kill_buffer[40];

  MYSQL *kill_mysql= NULL;

  /* terminate if no query being executed, or we already tried interrupting */

  if (!executing_query || interrupted_query++)

    goto err;

  kill_mysql= mysql_init(kill_mysql);

  if (!mysql_real_connect(kill_mysql,current_host, current_user, opt_password,

                          "", opt_mysql_port, opt_mysql_unix_port,0))

    goto err;

  /* kill_buffer is always big enough because max length of %lu is 15 */

  sprintf(kill_buffer, "KILL /*!50000 QUERY */ %lu", mysql_thread_id(&mysql));

  mysql_real_query(kill_mysql, kill_buffer, strlen(kill_buffer));

  mysql_close(kill_mysql);

  tee_fprintf(stdout, "Query aborted by Ctrl+C\n");

  return;

err:

#ifdef _WIN32

  mysql_thread_end();

  return;

#else

  mysql_end(sig);

#endif

}

 

       通過以上源碼可知,MySQL客戶端捕獲信號后,向服務器端發送KILL /*!50000 QUERY */ 命令並執行,從而將Query處理kill。

       基於以上兩個版本處理的源碼可知,MySQL客戶端一定會捕獲CTRL+C信號,並對該信號進行處理。而對於不同版本的客戶端,由於發送命令的不同,導致MySQL服務器端執行結果有所不同。

結論

       通過以上測試和源碼分析可知,MySQL客戶端肯定會捕獲CTRL+C信號,並對信號進行處理。不同的是,在MySQL 5.5.20版本的客戶端中執行時,服務器端執行KILL QUERY 命令,將QUERY KILL掉,並將連接關閉。而對MySQL 5.0.77客戶端中執行時,服務器端執行KILL /*!50000 QUERY */ 命令,KILL掉QUERY,但保持連接。

       此外,對於update、delete數據更新操作[1],CTRL+C會將執行的操作標記為KILLED狀態,然后執行回滾操作。因此,不會因為CTRL+C操作,導致數據變臟。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM