PyMySQL技术问询:获取最后SQL errno及连接池坏连接归还时剔除
嘿,针对你提出的两个PyMySQL相关问题,我结合实际开发经验来给你捋捋解决方案:
问题1:如何获取PyMySQL连接的最后一个SQL errno?
PyMySQL其实在多个层面都提供了获取错误编号的方式,核心分两种场景:
- 捕获异常时直接获取:当执行SQL触发
pymysql.MySQLError异常时,异常对象本身就携带了errno信息,你可以直接访问e.errno(PyMySQL的异常类已经封装了这个属性),也可以通过e.args[0]拿到。 - 主动检查连接状态:即使没有抛出异常,连接对象(
Connection)本身也有一个errno属性,会记录最近一次操作的错误编号,没有错误时该值为0。
给你写个实际代码示例:
import pymysql from pymysql import MySQLError try: # 建立连接 conn = pymysql.connect(host="localhost", user="root", password="your_password", db="test_db") cursor = conn.cursor() # 执行一个会出错的SQL(比如查询不存在的表) cursor.execute("SELECT * FROM non_existent_table") except MySQLError as e: # 从异常对象获取errno print(f"错误编号:{e.errno}") # 也可以从连接对象获取最近的错误编号 print(f"连接记录的错误编号:{conn.errno}") finally: if conn: conn.close()
问题2:连接归还至池时剔除已损坏连接,避免每次获取时ping
完全理解你的性能顾虑——每次获取连接都执行SELECT 1或者ping()确实会在高并发场景下带来额外开销。核心思路是在连接的生命周期内跟踪健康状态,归还时再做校验剔除,具体可以这么实现:
方案思路
- 给连接池中的每个连接维护一个「健康标记」,记录该连接是否在使用中出现过致命错误。
- 当连接在使用过程中触发了连接断开类的错误(比如errno 2006、2013这类),立即标记为「不健康」。
- 归还连接时,先检查健康标记和连接对象的
errno,如果是损坏状态,直接关闭连接并补充新连接到池,而不是放回池内。
代码示例(自定义连接池)
假设你用自定义连接池实现,这里给你一个简化版的参考:
import pymysql from pymysql import MySQLError from queue import Queue class CustomDBPool: def __init__(self, host, user, password, db, pool_size=5): self.pool = Queue(maxsize=pool_size) self.config = { "host": host, "user": user, "password": password, "db": db, "autocommit": True } # 初始化连接池,每个元素是(连接对象, 是否健康) for _ in range(pool_size): self.pool.put((self._create_conn(), True)) def _create_conn(self): """创建新的数据库连接""" return pymysql.connect(**self.config) def get_conn(self): """从池里获取连接,这里不需要额外ping""" conn, is_healthy = self.pool.get() # 极端兜底:如果归还时漏检了,这里再替换新连接 if not is_healthy: conn.close() conn = self._create_conn() is_healthy = True return conn, is_healthy def return_conn(self, conn, is_healthy): """归还连接时处理损坏连接""" # 先判断标记和连接的errno fatal_errnos = (2006, 2013, 2014, 2055) # 对应连接断开、超时等致命错误 if not is_healthy or conn.errno in fatal_errnos: # 损坏连接直接关闭,补充新连接到池 conn.close() self.pool.put((self._create_conn(), True)) else: # 健康连接放回池 self.pool.put((conn, True)) # 使用示例 if __name__ == "__main__": pool = CustomDBPool(host="localhost", user="root", password="your_password", db="test_db") conn, is_healthy = pool.get_conn() try: cursor = conn.cursor() cursor.execute("INSERT INTO test_table(name) VALUES('test')") except MySQLError as e: # 如果是致命连接错误,标记为不健康 if e.errno in (2006, 2013, 2014, 2055): is_healthy = False # 其他业务错误可以正常抛出处理 raise finally: pool.return_conn(conn, is_healthy)
补充说明
- 你可以根据MySQL官方的错误码列表,扩展
fatal_errnos的范围,覆盖所有你认为会导致连接损坏的错误编号。 - 这种方式只在连接出现异常或归还时做检查,避免了每次获取连接的额外开销,性能损耗几乎可以忽略。
内容的提问来源于stack exchange,提问作者Ke Lu
相关产品推荐
相关产品推荐

