如何检测MySQL临时故障?基于MySQL Connector/Python的重试咨询
基于MySQL错误码的临时故障重试方案
这确实是个很实际的痛点——MySQL Connector/Python的异常映射确实有点混乱,比如你提到的2013(丢连接)对应InterfaceError、2006(服务器断开)对应OperationalError,但两者都是需要重试的临时故障。其实不用纠结异常类型,直接通过**错误码(errno)**来判断才是最靠谱的方式。
核心思路:提取异常的errno属性
不管MySQL Connector抛出的是哪种PEP 249规范的异常,只要是MySQL相关的错误,异常对象都会携带errno属性,对应原生的MySQL错误码。我们可以直接读取这个值来分类错误。
第一步:定义可重试的错误码集合
根据MySQL官方文档和实际生产经验,以下这些错误码通常对应临时故障,值得重试:
- 2006:
Server has gone away(服务器断开连接) - 2013:
Lost connection to MySQL server during query(查询过程中丢失连接) - 2014:
Commands out of sync; you can't run this command now(命令同步错误,多是连接复用临时问题) - 2055:
Lost connection to MySQL server at '%s', system error: %d(连接超时) - 1040:
Too many connections(连接数超限,临时峰值可重试) - 1042:
Can't get hostname for your address(解析主机名失败,网络临时问题)
第二步:封装重试判断逻辑
把判断逻辑封装成函数,让代码更整洁:
def is_retryable_mysql_error(error): # 先确保是MySQL异常且包含errno属性 if not hasattr(error, 'errno'): return False # 可重试错误码集合 retryable_codes = {2006, 2013, 2014, 2055, 1040, 1042} return error.errno in retryable_codes
第三步:结合重试机制实现完整逻辑
推荐用指数退避的重试策略(每次重试间隔翻倍),避免短时间内大量请求压垮恢复中的服务器。下面是一个完整的示例:
import time from mysql.connector import errors, connect def is_retryable_mysql_error(error): if not hasattr(error, 'errno'): return False retryable_codes = {2006, 2013, 2014, 2055, 1040, 1042} return error.errno in retryable_codes def execute_with_retry(cnx, query, max_retries=3, initial_delay=1): retries = 0 delay = initial_delay while retries < max_retries: try: # 尝试获取游标并执行查询 cursor = cnx.cursor() try: cursor.execute(query) result = cursor.fetchall() return result finally: cursor.close() except errors.MySQLError as e: if is_retryable_mysql_error(e): print(f"遇到临时错误 [{e.errno}]: {e.msg},{delay}秒后重试...") time.sleep(delay) retries += 1 delay *= 2 # 指数退避 # 可选:如果是连接失效,尝试重新连接 if e.errno in (2006, 2013): cnx.reconnect() else: # 非临时错误,直接抛出(比如权限错误、表不存在等) raise # 重试耗尽仍失败,抛出异常 raise RuntimeError(f"重试{max_retries}次后仍无法执行查询") # 使用示例 if __name__ == "__main__": cnx = connect(host="your_host", user="your_user", password="your_pass", database="your_db") try: result = execute_with_retry(cnx, "SELECT * FROM your_table") print(result) finally: cnx.close()
额外注意事项
- 区分临时与永久错误:像1045(权限拒绝)、1146(表不存在)、1064(SQL语法错误)这类错误,重试多少次都没用,一定要排除在可重试列表外。
- 连接池场景:如果使用连接池管理连接,当获取游标失败时,除了判断错误码,还可以考虑从连接池中重新获取新连接,而不是复用旧连接。
- 异常捕获范围:捕获
errors.MySQLError可以覆盖所有MySQL相关的异常,比单独捕获InterfaceError或OperationalError更全面,避免遗漏其他带errno的临时异常。
内容的提问来源于stack exchange,提问作者Mathieu Dubois
相关产品推荐
相关产品推荐

