[转帖]MySQL中CTRL+C信号处理 _MySQL, Oracle及数据库讨论区_Weblogic技术|Tuxedo技术|中间件技术|Oracle论坛|JAVA论坛|Linux/Unix技术|hadoop论坛_联动北方技术论坛  
网站首页 | 关于我们 | 服务中心 | 经验交流 | 公司荣誉 | 成功案例 | 合作伙伴 | 联系我们 |
联动北方-国内领先的云技术服务提供商
»  游客             当前位置:  论坛首页 »  自由讨论区 »  MySQL, Oracle及数据库讨论区 »
总帖数
1
每页帖数
101/1页1
返回列表
0
发起投票  发起投票 发新帖子
查看: 2254 | 回复: 0   主题: [转帖]MySQL中CTRL+C信号处理         下一篇 
刘习托
注册用户
等级:中士
经验:208
发帖:66
精华:0
注册:2011-11-23
状态:离线
发送短消息息给刘习托 加好友    发送短消息息给刘习托 发消息
发表于: IP:您无权察看 2014-11-25 15:32:29 | [全部帖] [楼主帖] 楼主

目的

       为了理解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 <ID>命令,并且连接关闭。在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 <ID>命令发送到服务器端执行,从而将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 */ <ID>命令并执行,从而将Query处理kill。

       基于以上两个版本处理的源码可知,MySQL客户端一定会捕获CTRL+C信号,并对该信号进行处理。而对于不同版本的客户端,由于发送命令的不同,导致MySQL服务器端执行结果有所不同。

结论

       通过以上测试和源码分析可知,MySQL客户端肯定会捕获CTRL+C信号,并对信号进行处理。不同的是,在MySQL 5.5.20版本的客户端中执行时,服务器端执行KILL QUERY <ID>命令,将QUERY KILL掉,并将连接关闭。而对MySQL 5.0.77客户端中执行时,服务器端执行KILL /*!50000 QUERY */ <ID>命令,KILL掉QUERY,但保持连接。

       此外,对于update、delete数据更新操作[1],CTRL+C会将执行的操作标记为KILLED状态,然后执行回滚操作。因此,不会因为CTRL+C操作,导致数据变脏。

--转自 北京联动北方科技有限公司




赞(0)    操作        顶端 
总帖数
1
每页帖数
101/1页1
返回列表
发新帖子
请输入验证码: 点击刷新验证码
您需要登录后才可以回帖 登录 | 注册
技术讨论