Python 操作数据库 MySQL Timed out 和 select 也需要 commit

当用 Python 操作MySQL时,我们经常用的驱动是 mysql.connector

import mysql.connector
from mysql.connector import InterfaceError, OperationalError

这里需要强调的是:

  1. 如果 SQL 执行的是 ALTER 操作,则当客户端程序走到 cursor.execute(sql) 后:
    如果因为 SQL 返回超时,客户端抛异常,同时捕获异常后进行 close cursor 和 conn 最后程序结束。

    此时登录数据库服务器, 执行 show processlist 可以看到连接还存在了也就是 SQL 还在数据库服务端执行中。随着时间推移,大多数情况该 ALTER SQL 都能执行成功。这需要特别注意:客户端都异常退出了,SQL 还在服务端执行,最后还能执行成功,但是仅仅限于 DDL,因为其他类的 SQL 需要客户端的 commit 或者接受数据
    如果想改变这种行为,可以在客户端捕获异常后,快速建立一个新连接把这个连接 KILL 掉。

  2. 如果 SQL 执行的是 ALTER 操作,则当客户端程序走到 cursor.execute(sql) 后:
    如果打开另一个窗口执行 kill -9 PID, 然后登陆数据库 show processlist ,发现连接还在执行刚刚的 SQL

  3. 模拟 ALTER 超时:

    1.  在 console 1:  
     	a)用 begin 手动开启事务
     	b) select *  from  sql_exec_record_dbg  limit 1
    
    2. 在 console 2:
        python alter_poc.py 来变更 sql_exec_record_dbg  的列,同时设置超时时间 10s
    
    3. 在 console1:
       a) show processlist 可以看到,连接已经打过来了,状态是 Waiting for table metadata lock
       b) 等待大约 10s  让 console 2 的程序超时异常退出
       c) 用 commit; 手动提交事务,
       d) 可以看到状态为 Waiting for table metadata lock 的连接的 SQL 还是会被执行成功的
    
     	mysql> begin;
     	Query OK, 0 rows affected (0.00 sec)
     	mysql> 
     	mysql> select * from sql_exec_record_dbg limit 1 \G
     	*************************** 1. row ***************************
     	             id: 2625
     	        task_id: f79a95ae-e0d1-4d6a-88a4-f396da5066ca
     	   cluster_name: NULL
     	        db_host: 172.25.28.36
     	        db_port: 3307
     	        db_name: arch
     	     table_name: NULL
     	            sql: SELECT * FROM `tab1` where 1=1 and `created_date` >= '2019-05-10 14:50:08' and `created_date` < '2019-05-11 00:00:00' limit 100
     	      trans_row: 100
     	         status: 1
     	create_datetime: 2019-05-13 03:35:32.515228
     	update_datetime: 2019-05-13 03:35:32.786594
     	        std_err: NULL
     	    sub_rule_id: 180
     	 parent_rule_id: 5
     	1 row in set (0.00 sec)
     	mysql> 
     	mysql> show processlist;
     	+----------+-----------+--------------------+----------+---------+------+----------+------------------+
     	| Id       | User      | Host               | db       | Command | Time | State    | Info             |
     	+----------+-----------+--------------------+----------+---------+------+----------+------------------+
     	| 50923359 | inception | 172.25.28.38:49664 | archiver | Sleep   |    1 |          | NULL             |
     	| 50923433 | inception | 172.25.28.38:50048 | archiver | Query   |    0 | starting | show processlist |
     	+----------+-----------+--------------------+----------+---------+------+----------+------------------+
     	2 rows in set (0.00 sec)
     	mysql> 
     	mysql> show create table sql_exec_record_dbg \G
     	*************************** 1. row ***************************
     	       Table: sql_exec_record_dbg
     	Create Table: CREATE TABLE `sql_exec_record_dbg` (
     	  `id` int(11) NOT NULL AUTO_INCREMENT,
     	   ......
     	  `sql` varchar(355) COLLATE utf8_bin DEFAULT NULL,
     	  `trans_row` int(10) unsigned NOT NULL,
     	  ......
     	) ENGINE=InnoDB AUTO_INCREMENT=3464769 DEFAULT CHARSET=utf8 COLLATE=utf8_bin
     	1 row in set (0.00 sec)
     	mysql> 
     	mysql> show processlist;
     	+----------+-----------+--------------------+----------+---------+------+---------------------------------+------------------------------------------------------------------------+
     	| Id       | User      | Host               | db       | Command | Time | State                           | Info                                                                   |
     	+----------+-----------+--------------------+----------+---------+------+---------------------------------+------------------------------------------------------------------------+
     	| 50923359 | inception | 172.25.28.38:49664 | archiver | Sleep   |    0 |                                 | NULL                                                                   |
     	| 50923433 | inception | 172.25.28.38:50048 | archiver | Query   |    0 | starting                        | show processlist                                                       |
     	| 50923528 | inception | 172.25.28.38:50538 | archiver | Query   |    6 | Waiting for table metadata lock | alter table sql_exec_record_dbg change column `sql` `sql` varchar(400) |
     	+----------+-----------+--------------------+----------+---------+------+---------------------------------+------------------------------------------------------------------------+
     	3 rows in set (0.00 sec)
     	mysql>
     	mysql> commit;
     	Query OK, 0 rows affected (0.00 sec)
     	mysql> 
     	mysql> show create table sql_exec_record_dbg \G
     	*************************** 1. row ***************************
     	       Table: sql_exec_record_dbg
     	Create Table: CREATE TABLE `sql_exec_record_dbg` (
     	  `id` int(11) NOT NULL AUTO_INCREMENT,
     	   ......
     	  `sql` varchar(400) COLLATE utf8_bin DEFAULT NULL,
     	  `trans_row` int(10) unsigned NOT NULL,
     	  ......
     	) ENGINE=InnoDB AUTO_INCREMENT=3464769 DEFAULT CHARSET=utf8 COLLATE=utf8_bin
     	1 row in set (0.00 sec)
     	mysql> 
    
  4. 当客户端发起的 SQL 在执行时,登录数据库手动 KILL 连接:客户端抛的异常是
    mysql.connector.errors.InterfaceError

  5. 客户端发起的 SQL 执行超时时,客户端抛的异常是: mysql.connector.errors.OperationalError

  6. MySQL select 也是需要 commit 的,为了避免长事务或者如果是 select * from table where id = 1 for update 这更需要执行 conn.commit()

你可能感兴趣的:(数据库,Python)