sql.Open 不建立连接,必须紧接 db.Ping() 验证;DSN 需 url.QueryEscape 密码、含 tcp() 和 parseTime=true+loc=Local;Query 后必调 rows.Next() 和 rows.Close();NULL 值须用 sql.NullString 等类型;超时需用 context 控制。

sql.Open 之后必须立刻 db.Ping()
很多 panic 实际发生在第一次查询时,不是 sql.Open 那一刻。它只解析 DSN、注册驱动、配置连接池,不建 TCP 连接。你写 db, err := sql.Open("mysql", dsn) 即使 MySQL 完全宕机、密码错、端口填成 3307,err 也几乎总是 nil。
真正暴露问题的只有 db.Ping() —— 它会强制获取一个连接并握手。上线前漏掉这步,服务启动后第一个请求就崩。
- 必须紧接在
sql.Open后调用:if err := db.Ping(); err != nil { log.Fatal(err) } - 别把
db.Ping()放在 HTTP handler 里做“懒校验”,它该是初始化阶段的硬性检查 - 如果用容器部署,
Ping()失败常伴随dial tcp: lookup mysql: no such host(DNS 未配置)或connection refused(端口没暴露/没映射)
DSN 字符串里 parseTime=true 和 loc=Local 必须成对出现
漏掉 parseTime=true,查 DATETIME 字段时 Scan() 会直接 panic:unsupported Scan, storing driver.Value type []uint8 into type *time.Time。这不是类型写错,是驱动根本没启用时间解析。
loc=Local 不是可选参数:MySQL 返回的时间戳默认按服务器时区解释,Go time.Time 默认 UTC。不加 loc,上海服务器存的 2026-04-27 14:00:00 在 Go 里可能变成 2026-04-27 06:00:00 +0000 UTC。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
立即学习“go语言免费学习笔记(深入)”;
- 开发环境用
loc=Local最省事;生产建议统一用loc=UTC,应用层再转本地时区 - 密码含
@或/时,必须先url.QueryEscape(),否则 DSN 解析失败 - 别写
mysql://或漏掉tcp()—— 正确格式只能是user:pass@tcp(127.0.0.1:3306)/dbname?parseTime=true&loc=Local
Query 多行时 rows.Next() 和 rows.Close() 缺一不可
db.Query() 返回 *sql.Rows,它背后绑着一个连接。跳过 rows.Next() 直接 Scan() 会 panic:sql: Scan called without calling Next。而漏掉 rows.Close(),连接永远不归还池子,SetMaxOpenConns 很快被耗尽,后续所有查询卡死或报 too many connections。
- 标准写法必须带
defer rows.Close(),且放在if err != nil检查之后 - 循环结束后要显式检查
rows.Err(),否则磁盘 I/O 错误、网络中断会被静默吞掉 - 即使
rows.Next()一次都没进(结果为空),rows.Close()仍需调用
NULL 值必须用 sql.NullString 而不是 *string
MySQL 字段允许为 NULL,但 Go 原生类型如 string、int64 无法表示 “有值” 和 “数据库 NULL” 的区别。直接 Scan(&name) 到 string,遇到 NULL 就 panic:sql: Scan error on column index X: unsupported Scan, storing driver.Value type <nil> into type *string</nil>。
别用 *string 替代 —— database/sql 不支持 Scan 到未初始化指针,且业务层判断 name == nil 语义模糊(是没查到?还是字段真为 NULL?)。
- 字段声明必须是
sql.NullString,扫描后通过ns.Valid判断是否为 NULL,再取ns.String - 同理,整数用
sql.NullInt64,布尔用sql.NullBool - 如果结构体字段多且 NULL 频繁,考虑用
sqlx的StructScan,它自动适配sql.Null*类型
SetMaxOpenConns(20) 就能防慢查询拖垮服务——真正的超时控制只能靠 db.QueryContext() 配合 context.WithTimeout()。

















