sql.open仅校验dsn格式,不建立真实连接;必须显式调用db.ping()验证账号、网络及数据库可达性,否则后续query会报dial timeout或access denied。

sql.Open 不会立即连上数据库,它只做参数校验;真正建立连接靠 db.Ping() —— 这是绝大多数新手踩坑的第一步。
为什么 sql.Open 成功了却查不到数据?
因为 sql.Open 返回的是一个连接池句柄,不是物理连接。它不验证账号密码、网络通不通、库是否存在,只检查 DSN 格式是否合法。
- 错误现象:
sql.Open无错,但后续db.Query或db.Ping()报dial tcp: i/o timeout或access denied - 必须显式调用
db.Ping()才能暴露真实连接问题 - 建议在初始化后立即加
if err := db.Ping(); err != nil { log.Fatal(err) } - 别把
defer db.Close()放在db.Ping()前面,否则连接池可能刚建好就被关了
parseTime=true 和 loc=Local 必须配对用
MySQL 的 TIMESTAMP 和 DATETIME 在 Go 中默认解析为 string,除非你明确告诉驱动要转成 time.Time。
-
parseTime=true开启时间解析,否则Scan到time.Time变量会 panic -
loc=Local指定时区,否则 MySQL 返回的 UTC 时间会被按本地时区解释(常见于时间错位 8 小时) - 推荐完整 DSN 参数:
root:pass@tcp(127.0.0.1:3306)/demo?charset=utf8mb4&parseTime=true&loc=Local - 注意:URL 中的
&是 HTML 实体,在 Go 字符串里写成&或直接用&(若非 HTML 上下文)
用 Exec 还是 Prepare?看场景
不是所有 SQL 都该预编译。滥用 Prepare 可能引入连接泄漏或性能损耗。
- 单次执行的语句(如建表、初始化数据)直接用
db.Exec("CREATE TABLE ...") - 高频重复执行的语句(如用户登录校验、订单插入)才用
db.Prepare,避免每次解析 SQL -
Prepare返回的*sql.Stmt必须手动Close(),否则底层连接不会归还池中 - 更安全的做法:用
db.QueryRow/Query/Exec的变体(如db.QueryRowContext),它们内部已做预处理优化
真正容易被忽略的,是连接池默认参数——db.SetMaxOpenConns(0) 表示不限制,但在高并发下可能耗尽 MySQL 连接数;生产环境务必设 SetMaxOpenConns 和 SetMaxIdleConns,并配合监控观察 db.Stats() 中的 OpenConnections。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











