Golang Goroutine操作MySQL遇连接超限问题求助
问题分析与修复方案
代码核心问题
- 每个goroutine创建独立连接池:
getData函数中每次执行sql.Open都会新建一个数据库连接池,每个池还设置了8个最大连接。5000个goroutine同时启动会瞬间创建海量连接,直接触发MySQL的连接数上限。 - 连接池配置完全无效:局部
db实例的连接池参数(SetMaxOpenConns/SetMaxIdleConns)无法全局生效,起不到限流作用。 - 资源管理混乱:每个goroutine结束时调用
db.Close()关闭整个连接池,导致连接频繁创建销毁,引发connection reset by peer错误。
修复后的代码
type Page struct { Stat int } func main() { cfg := mysql.Config{ // 你的数据库配置 } // 初始化全局共享连接池 db, err := sql.Open("mysql", cfg.FormatDSN()) checkError(err) // 根据MySQL的max_connections配置合理调整,示例设为30 db.SetMaxOpenConns(30) db.SetMaxIdleConns(10) db.SetConnMaxLifetime(time.Minute * 3) defer db.Close() // 程序退出时关闭全局连接池 // 5000个group id groups := []int{} // 信号量控制并发数,和连接池最大打开数匹配 sem := make(chan struct{}, 30) pages := make(chan Page, len(groups)) for _, id := range groups { sem <- struct{}{} go func(id int) { defer func() { <-sem }() count, err := getData(id, db) checkError(err) pages <- Page{Stat: count} }(id) } // 收集所有结果 for range groups { page := <-pages fmt.Println(page.Stat) } } func getData(i int, db *sql.DB) (int, error) { rows, err := db.Query(`select g.id from goods as g where g.groupid = ?`, i) if err != nil { return 0, err } defer rows.Close() count := 0 var id1 int // 修复原代码未声明变量的问题 for rows.Next() { err = rows.Scan(&id1) if err != nil { return 0, err } count++ } // 检查结果集遍历后的潜在错误 if err = rows.Err(); err != nil { return 0, err } return count, nil } func checkError(err error) { if err != nil { panic(err) } }
关键修复说明
- 全局复用连接池:在
main中仅初始化一次*sql.DB,所有goroutine共享该连接池,连接池参数才能真正控制整体连接数量。 - 并发限流:通过信号量限制goroutine并发数,避免连接池过载,并发数建议和
SetMaxOpenConns值保持一致。 - 补全变量声明与错误处理:修复原代码中
id1未声明的编译错误,增加rows.Err()检查避免遗漏结果集遍历错误。
内容的提问来源于stack exchange,提问作者Алексей Морозов
相关产品推荐
相关产品推荐

