XORM高并发下需协同连接池生命周期、错误码识别与主动健康检查:设SetMaxOpenConns、SetMaxIdleConns、SetConnMaxLifetime(如3小时)、SetConnMaxIdleTime(如30分钟),并手动PingContext探活,仅对幂等SELECT操作指数退避重试。

在高并发场景下使用XORM访问MySQL时,连接超时和断线重连不是靠“重试一次就恢复”的简单逻辑,而是必须结合连接池生命周期、驱动错误码识别与主动健康检查三者协同处理,否则会出现大量CLOSED_WAIT连接堆积或查询卡死。
识别真正的连接断开错误
不能用err != nil就认为要重连——这可能是SQL语法错、主键冲突、字段为空等业务错误。必须穿透XORM封装,对错误做类型断言:
方法一:PostgreSQL驱动(github.com/lib/pq)
用errors.As(err, &pqErr)提取*pq.Error,检查pqErr.Code是否以"08"开头(如"08006"表示连接被拒绝);
方法二:MySQL驱动(github.com/go-sql-driver/mysql)
断言为*mysql.MySQLError,检查Number()是否等于2003(无法连接服务器)、2013(查询中连接丢失)或2002(无法连接本地MySQL服务);
【关键点】若错误字符串含"dial tcp"、"i/o timeout"、"connection refused",且类型断言失败,大概率是网络层中断,可作为兜底判断依据。
配置连接池避免连接数失控
仅调SetMaxOpenConns(50)和SetMaxIdleConns(5)无法阻止ESTABLISHED连接暴涨——因为MySQL服务端wait_timeout默认8小时,而Go连接池若不设过期策略,会一直尝试复用已失效的TCP连接。
第一步:设置最大打开连接数
SetMaxOpenConns应设为MySQL服务端max_connections的60%~80%,例如SHOW VARIABLES LIKE 'max_connections'返回200,则设为120~160;
第二步:限制空闲连接上限
SetMaxIdleConns必须≤SetMaxOpenConns,否则会被静默截断;建议设为SetMaxOpenConns的50%左右;
第三步:强制连接定期刷新
调用SetConnMaxLifetime(3 * time.Hour),确保连接在池中存活不超过3小时,避免被NAT网关或SLB在3~5分钟无流量后单向断开却未回收;
第四步:启用空闲连接最大存活时间(Go 1.15+)
SetConnMaxIdleTime(30 * time.Minute),让空闲连接更早释放,缓解CLOSED_WAIT堆积。
主动探活与幂等重试
database/sql连接池不会自动Ping数据库,XORM也不提供内置探活机制,必须手动触发:
每次执行关键查询前,先用ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)控制探活耗时,再调用engine.DB().PingContext(ctx);
若PingContext返回错误,且该错误被识别为网络类异常(如2003/2013或"i/o timeout"),则进入重试流程;
重试只针对SELECT类幂等操作,采用指数退避:第1次等待1s → 第2次等待2s → 第3次等待4s,最多3次;
【不可逆操作】INSERT/UPDATE/DELETE绝不可无条件重试,必须配合业务唯一ID或状态校验,否则会导致重复写入。











