sql.open仅初始化连接池不校验连通性,需显式调用db.ping();parsetime=true必配否则time.time扫描失败;连接池参数在*sql.db设置,须合理配置maxopenconns等;query后必须rows.close()防连接泄漏。

Go 连 MySQL 不是配好驱动就能用,关键在 sql.Open 只初始化连接池,不校验连通性——你得主动调用 db.Ping() 才知道是不是真通。
为什么 sql.Open 不报错但查询失败?
这是最常踩的坑:sql.Open 只解析 DSN、设置连接池参数,并不建立实际 TCP 连接。哪怕 MySQL 完全没开、密码写错、端口填成 3307,sql.Open 都返回 *sql.DB 且 err == nil。
- 真正首次建连发生在第一次执行
db.Query、db.Exec或显式调用db.Ping()时 - 错误会延迟抛出,导致 panic 出现在业务逻辑里,而非初始化阶段
- 上线前务必加
if err := db.Ping(); err != nil { log.Fatal(err) }
parseTime=true 和 loc=Local 怎么配才不出错?
MySQL 默认把 DATETIME 当字符串返回,不转 Go 的 time.Time;而时区处理不当会导致时间偏移 8 小时(尤其用 Asia/Shanghai 时)。
-
parseTime=true是必须加的,否则SELECT created_at FROM users返回的是[]byte,不是time.Time -
loc=Local表示用本机时区解析时间,适合本地开发;生产环境建议用loc=UTC+ 应用层统一转换 - 漏掉
parseTime=true会报错:sql: Scan error on column index 0, name "created_at": unsupported Scan, storing driver.Value type []uint8 into type *time.Time
连接池参数该在哪设?常见误配点有哪些?
连接池参数在 *sql.DB 上设置,不是 DSN 里。不设或乱设容易耗尽 MySQL 连接数(报错:ERROR 1040 (HY000): Too many connections)。
-
db.SetMaxOpenConns(25):控制最大并发连接数,别盲目设太高(云数据库常限制更严) -
db.SetMaxIdleConns(25):空闲连接上限,建议 ≤MaxOpenConns -
db.SetConnMaxLifetime(5 * time.Minute):避免连接长期空闲被 MySQLwait_timeoutkill(云 DB 默认常短于 28800 秒) - 别在每次 HTTP 请求里
sql.Open,复用全局*sql.DB实例
查询后忘记 rows.Close() 会怎样?
db.Query 得到 *sql.Rows 后,即使循环读完所有数据,也必须显式调用 rows.Close()。否则底层连接不会归还给连接池。
- 典型错误写法:
rows, _ := db.Query("SELECT id FROM users"); for rows.Next() { /* ... */ }—— 缺少defer rows.Close()或rows.Close() - 后果:
MaxOpenConns很快耗尽,后续请求卡住或报dial tcp: i/o timeout - 正确模式:先检查
err,再defer rows.Close(),最后用rows.Err()检查遍历过程是否出错
真正麻烦的从来不是连上,而是连上之后查不出数据却没报错——比如字段类型和 Scan 目标不匹配、NULL 值没用 sql.NullString 接收、或者事务没 Commit 导致另一端看不见变更。这些细节不提前压住,线上排查成本远高于初始化多写三行校验代码。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











