From 0e7804451bae60f69377ce54aa2b975adca4af53 Mon Sep 17 00:00:00 2001 From: Franklin-F Date: Tue, 15 Jul 2025 21:08:35 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=20MySQL=20=E9=87=8D?= =?UTF-8?q?=E8=AF=95=E6=9C=BA=E5=88=B6=EF=BC=8C=E5=A2=9E=E5=8A=A0=E5=8F=AF?= =?UTF-8?q?=E9=87=8D=E8=AF=95=E9=94=99=E8=AF=AF=E7=B1=BB=E5=9E=8B=E5=B9=B6?= =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=97=A5=E5=BF=97=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DB.py | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/DB.py b/DB.py index c9815d3..61ee9ae 100644 --- a/DB.py +++ b/DB.py @@ -92,34 +92,44 @@ video_author = Table( ) -def mysql_retry(max_retries: int = 3, base_delay: float = 2.0): - """ - 装饰器:捕获断线异常(InterfaceError 或 OperationalError: 2013)后尝试重连,指数回退重试。 - """ +def mysql_retry(max_retries: int = 3, base_delay: float = 1.0): + RETRIABLE_ERRORS = {2013, 1213, 2006} def decorator(fn): @functools.wraps(fn) def wrapper(self, *args, **kwargs): for attempt in range(1, max_retries + 1): try: + # 确保连接仍存活,失败自动 reconnect self.conn.ping(reconnect=True) return fn(self, *args, **kwargs) - except (pymysql.InterfaceError, pymysql.OperationalError) as e: - if isinstance(e, pymysql.OperationalError) and e.args[0] != 2013: - raise # 只处理 2013,其他 OperationalError 抛出 - wait = base_delay * (2 ** (attempt - 1)) - logger.warning(f"[MySQL][{fn.__name__}] 第{attempt}次重试(断开连接:{e}),等待 {wait:.1f}s 后重连…") - time.sleep(wait) - self._reconnect_mysql() - if attempt == max_retries: - logger.error("[MySQL] 重试多次仍失败,抛出异常") + + except pymysql.OperationalError as e: + errno = e.args[0] + if errno not in RETRIABLE_ERRORS: raise + reason = { + 2013: "连接断开", + 1213: "死锁冲突", + 2006: "连接失效", + }.get(errno, f"MySQL错误{errno}") + + wait = base_delay * (2 ** (attempt - 1)) + logger.warning(f"[MySQL][{fn.__name__}] 第{attempt}次重试({errno} {reason}):{e},等待 {wait:.1f}s...") + + # 仅对断连类错误尝试重连 + if errno in {2013, 2006}: + self._reconnect_mysql() + + time.sleep(wait) + + logger.error(f"[MySQL] 函数 `{fn.__name__}` 重试 {max_retries} 次仍失败,最终异常:{e}") + raise + return wrapper - return decorator - def redis_retry(max_retries: int = 3): """ 装饰器工厂:指定最大重试次数。