我用pymysql在MySQL执行查询,为什么关闭连接后进程还在?

I use pymysql to execute query in MySQL, but why the process is still alive after I close the connection?

我使用pymysql连接mysql,连接时我设置了一个read_timeout

conn = pymysql.connect(host=host,port=port,user=user,passwd=passwd,read_timeout=60)

我的查询代码在这里:

try:
    cur.execute(sql)
    rows = cur.fetchall()
except:
    # timeout
    logging.error()
finally:
    cur.close()
    conn.close()

当我执行长时间查询时,代码会引发超时错误,最后 cursorconnection 都关闭(我调试 conn._closed 以确认连接已关闭) .但是当我登录到mysql和show processlist时,我发现进程还活着。

我的问题是,这是否正常?如何在我的代码中查询超时时终止进程?我想出了一个在查询之前执行 set session max_execution_time=60 的解决方法,但我认为这不是最佳做法。

这里是show processlist输出,我用sleep表示长时间查询,我想结果是一样的,connection在我的代码中关闭了,但是进程还在那里。

show processlist output

您需要在关闭连接之前提交事务。由于事务尚未提交到数据库,因此该进程必须处于活动状态。 在开头或 conn 初始化期间添加 conn.autocommit=true

conn = pymysql.connect(host=host,port=port,user=user,passwd=passwd,read_timeout=60, autocommit=True)

或者,您可以在关闭连接之前使用conn.commit()

PyMySQL 连接对象上的

read_timeout 在执行 timeout value on the underlying socket during read operations. If the timeout is reached, the socket raises a subclass of OSError (socket.timeout), which is caught in the connection's _read_bytes method.
During the handling of that exception, _force_close 时使用,最终将连接的 self._sock 属性设置为 None

当您的代码到达 finally 块时,在执行 conn.close() 期间剩下要做的就是将 _closed 设置为 True。
如果此时 _sock 不是 Noneclose 方法也会尝试通过套接字将 COM_QUIT 发送到服务器,但是由于客户端套接字已经关闭,那不会发生。

从服务器的角度来看,客户端无声地断开了连接。不幸的是,很难找到有意义的文档来说明这种情况下到底发生了什么。

通常,在连接超时时,挂起的事务应该被回滚。但是,我不确定客户端关闭其套接字是否会导致服务器端立即超时,如 this Q&A 中所建议的那样。
此外,使用 MySQL 的 sleep 函数进行测试可能不是一个好主意。它可以完全挂起处理您的连接和查询的线程,使其无法及时检测到丢失的连接。

您可能想看看 MySQL 的 kill command, that is also implemented in the PyMySQL connection,尽管没有记录。但是,当执行 cursor.execute 语句可能需要很长时间才能完成时,这将需要在脚本中设置外部超时。