必须立即调用DB.Ping()验证连通性,否则首个HTTP请求才暴露dial tcp超时或access denied错误;正确顺序为sql.Open→设连接池参数→DB.Ping()→注册Iris依赖→app.Run()。

sql.Open之后必须立刻DB.Ping()
不调用 DB.Ping() 就直接跑 HTTP 服务,等于把连接失败的报错往后压——可能第一个用户请求时才暴露 dial tcp: i/o timeout 或 access denied,堆栈里还混着 Iris 的路由逻辑,根本看不出是 DB 初始化问题。Iris 的 app.Run() 是阻塞的,所有依赖数据库的初始化(比如预热缓存、加载配置表)都得在它之前做完。
所以正确顺序只能是:sql.Open → 设置 SetMaxOpenConns/SetMaxIdleConns → DB.Ping() → 注册到 Iris(如 app.RegisterDependency(db))→ app.Run()。
SetMaxOpenConns 和 SetMaxIdleConns 别乱设
这两个值不是越大越好,它们直接受限于 MySQL 服务端的 max_connections 和你应用的实际并发模型。
常见踩坑点:
-
SetMaxOpenConns(100)但 MySQL 配置了max_connections = 50→ 大量请求卡在连接池等待,表现为超时或ERROR 1040 (HY000): Too many connections -
SetMaxIdleConns(50)但实际 QPS 很低 → 空闲连接长期不释放,MySQL 端积压大量Sleep状态线程,白白占资源 - Iris handler 每次调
db.Query或db.Exec都会从池里取连接,不是每个请求独占一个,所以并发数 ≠ 连接数
推荐起始值:SetMaxIdleConns(5)、SetMaxOpenConns(20);上线后看 SHOW PROCESSLIST 和应用日志里的连接等待时间再调。
主从分离不能靠 Iris 自动路由
Iris 本身不介入数据库读写分离逻辑,它只管把 *gorm.DB 或 *sql.DB 实例注入进去。主从路由必须由你选用的 ORM 层实现:
比如 GORM v2 支持配置主库(Write)和从库(Read)两个连接池,然后按方法语义自动分发:
-
Create/Update/Delete→ 主库 -
Find/First默认走从库 - 事务内全部走主库
- 强一致读(比如刚写完立刻查)需显式调
db.WithContext(context.WithValue(ctx, gorm.SessionKey, &gorm.Session{Write: true}))或用Session(&gorm.Session{Write: true})
别指望 Iris 自己识别 SELECT 还是 UPDATE 并做路由——它连 SQL 都不碰。
高并发抽奖场景下,光调优连接池不够
连接池只是基础,真正扛不住高并发的是业务逻辑层的原子性保障。
例如抽奖扣库存,如果只写 UPDATE prize SET stock = stock - 1 WHERE id = ? AND stock > 0,在 MySQL RR 隔离级别下仍可能超卖:多个事务同时读到旧快照,都判断 stock > 0 成立,结果一起扣减。
必须配合:
- Redis 预扣库存(
DECR+EXPIRE),失败直接拒掉 - MySQL 行锁 + 条件更新(
SELECT ... FOR UPDATE)在事务中串行化关键路径 - 分布式锁(如 Redis
SET key value NX PX 1000)保护热点奖品 - 幂等 Key(如用户 ID + 活动 ID + 时间戳哈希)防止重放
连接池设再大,也救不了没加锁、没预检、没幂等的业务代码。


















