使用gorm不当出现too Many Connections的问题
文章目录
一个花费几小时排除问题的教训
前言
业务中使用 golang+gin+gorm 开发,最近新上线了一个版本,发现在日活用户数只有几百的时候数据库频繁出现too many connections
的错误,执行show processlist;
查看数据库进程都在干嘛,发现了大量连接处于 sleep 的状态。
排查
-
首先想到的是不是事务没提交,检查了一遍代码没有发现未提交的情况
-
show variables like '%max_connections%';
查看数据库最大连接数为 2000 多,不是数据库设置的问题 -
使用 gorm设置空闲连接数,并发数和连接超时
1 2 3
mysql.DB.DB().SetMaxIdleConns(50) mysql.DB.DB().SetMaxOpenConns(50) mysql.DB.DB().SetConnMaxLifetime(time.Minute)
重启服务观望了一会,发现连接数并没有减少。
-
由于 gorm 每次执行完会自动释放连接的,有点怀疑是这里出问题了,在本地测试了一下,疯狂发起请求多次之后发现连接数没有涨,不是 gorm 的问题
-
走到这里,只能想到还是代码出问题了。。仔细查找连接数上涨的同时用户调用的具体接口,查看具体的业务代码,发现好几处查询使用了 db.Where(xxx).Rows()
1 2 3 4 5 6 7 8
rows, err := db.Select("abs(sum(money)) as money").Rows() if err != nil { return } if rows.Next() { err = rows.Scan(&totalMoney) }
由于 gorm 无法 scan接收一个基础类型,这里用 rows 来处理单个值的查询结果。
这里用了
if rows.Next()
会导致roew 结果集没有被取完,而且最后也没用主动关闭,造成连接数无法释放修改为正确的姿势,解决了问题
1 2 3 4 5 6 7 8 9
rows, err := db.Select("abs(sum(money)) as money").Rows() if err != nil { return } defer rows.Close() for rows.Next() { err = rows.Scan(&totalMoney) }
测试中发现如果只使用
for rows.Next()
,不主动rows.CLose()
,gorm 也会主动帮你 close 掉连接的
###小结
还是太年轻,对数据库操作不够仔细,使用这种需要close 的 api 一定要注意 close。