sql.open仅初始化连接池不验证连通性,必须调用db.ping()才能发现mysql不可达;dsn需严格格式并启用parsetime=true以正确扫描time.time,loc=local适合开发而生产建议loc=utc;db.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 出现在业务逻辑里,而非初始化阶段
- DSN 格式必须严格:用户名:密码@tcp(地址:端口)/数据库名?charset=utf8mb4&parseTime=True&loc=Local
DSN 中 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,又直接 Scan 到time.Time,会报错:sql: Scan error on column index 0, name "created_at": unsupported Scan, storing driver.Value type []uint8 into type *time.Time
如何安全关闭连接并释放资源?
db.Close() 不是“断开一次连接”,而是关闭整个连接池、释放所有空闲连接,并拒绝新请求——它应该只在应用退出前调用一次。
- 日常操作完全不需要手动
Close()单条连接;*sql.DB是长生命周期对象,设计就是复用的 - 忘记调用
db.Ping()验证连接,又没设SetMaxOpenConns,高并发下可能耗尽 MySQL 连接数(报错:ERROR 1040 (HY000): Too many connections) - 推荐初始化后立刻验证:
db, err := sql.Open("mysql", dsn) if err != nil { log.Fatal(err) } if err = db.Ping(); err != nil { // 关键! log.Fatal("failed to connect to MySQL:", err) }
真正麻烦的从来不是连上,而是连上之后查不出数据却没报错——比如字段类型和 Scan 目标不匹配、NULL 值没用 sql.NullString 接收、或者事务没 Commit 导致另一端看不见变更。这些不会在 db.Ping() 里暴露,得靠日志和针对性测试。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











