← 返回博客
go2026-09-12 22:37:471 分钟 · 406 0

Gin + MySQL 进阶:连接池调优与读写分离

用 GORM/sql.DB 的连接池参数控住并发连接,讲清连接泄漏、context 超时,再用主从读写分离分摊查询压力。

#mysql#gin#连接池

1. 为什么单独讲

第 7 篇的 GORM 只讲了 CRUD,连接池没碰。默认连接数不限,流量一上来 MySQL 被连爆,或者连太多拖慢整体。生产第一道坎就是连接池。

2. 连接池参数

sql.DB(GORM 底层)是连接池,三个数定生死:

db, _ := gorm.Open(mysql.Open(dsn))
sqlDB, _ := db.DB()
sqlDB.SetMaxOpenConns(50)                 // 最大打开连接,按 max_connections 和实例数摊
sqlDB.SetMaxIdleConns(10)                 // 空闲保活数
sqlDB.SetConnMaxLifetime(1 * time.Hour)   // 连接最长寿命,防空闲被中间件断

MaxOpenConns 别超过 MySQL 的 max_connections 除以实例数,否则报「too many connections」。

3. 连接泄漏

最常见的坑:查完没关 rows,连接不归还池:

rows, err := db.Raw("...").Rows()
if err != nil {
    return
}
defer rows.Close() // 必须关,否则连接泄漏

GORM 的 Find/First 自动关,手写 Rows() 要自己 defer Close()

4. context 超时救连接

下游慢查询占着连接不放,池被耗光。带超时的查询(第 3 篇)让慢查询主动释放:

ctx, cancel := context.WithTimeout(ctx, 800*time.Millisecond)
defer cancel()
db.WithContext(ctx).Find(&orders)

5. 读写分离

写走主库,读走从库,分摊压力:

var db *gorm.DB     // 主库
var dbRead *gorm.DB // 从库

func (r *OrderRepo) Find(id string) (*Order, error) {
    return findOn(dbRead, id) // 读走从库
}
func (r *OrderRepo) Save(o *Order) error {
    return saveOn(db, o) // 写走主库
}

从库有复制延迟,刚写立马读可能读不到(最终一致),关键读(如刚下单查状态)走主库。

6. 慢查询与索引

db.Debug() 打印 SQL,慢的在 MySQL 开慢查询日志抓。EXPLAIN 看是否走索引。博客另有 MySQL 索引三连(btree/聚簇/写陷阱)可对照。

7. 上线清单

MaxOpenConns 按 max_connections 和实例数摊,别超限。手写 Rows()defer Close,防连接泄漏。查询带 context 超时,慢查询主动释放连接。读多写少做读写分离,但刚写的关键读走主库防延迟。开慢查询日志 + EXPLAIN 定期查慢 SQL。

下一篇讲权限,把第 6 篇的认证补上授权。

相关推荐

本文为原创文章,采用CC BY-NC-SA 4.0协议授权,转载请保留署名与原文链接。原文链接:https://www.wxbuluo.com/article/207