sql.Open不报错但查询失败是因为它只校验参数格式、不建立实际连接,真正连接延迟到首次Query或Ping;应初始化后立即调用db.Ping()探测连通性。

sql.Open 为什么没报错但后续查询失败
因为 sql.Open 只校验参数格式、不真正连接数据库,实际连接被延迟到第一次 db.Query 或 db.Ping 时才触发。
- 常见错误现象:
sql.Open返回nilerror,但紧接着db.Query报dial tcp: lookup xxx: no such host或connection refused - 正确做法:初始化后立刻调用
db.Ping()主动探测连接是否可用,别等业务逻辑里炸出来 - 注意
db.Ping()是阻塞的,超时由db.SetConnMaxLifetime和底层驱动决定;生产环境建议配合context.WithTimeout包一层
QueryRow 扫描 NULL 值时 panic 怎么办
Go 的 database/sql 对 SQL NULL 没有自动映射,直接往非指针变量里扫会 panic —— 比如用 int 接可能为 NULL 的列。
- 必须用对应类型的指针或
sql.Null*类型:例如*int、sql.NullString、sql.NullInt64 -
sql.NullString要手动检查.Valid字段,不能直接用.String,否则空值时返回空字符串(易掩盖逻辑错误) - 如果字段可能为空且你不想写一堆
if row.Scan(&v); v.Valid { ... },考虑用第三方库如github.com/georgysavva/scany,它支持结构体字段自动处理NULL
Exec 和 Query 区别不清导致事务出问题
db.Exec 用于无结果集操作(INSERT/UPDATE/DELETE),db.Query 用于带结果集的 SELECT;混用不仅语义错,还可能让连接池卡住或事务状态异常。
- 执行 INSERT 并想获取自增 ID?用
result.LastInsertId(),别用QueryRow("INSERT ... RETURNING id")—— 后者在 MySQL 不支持RETURNING,PostgreSQL 虽支持但要用QueryRow,不是Exec - 事务内误用
db.Query执行 UPDATE:不会报错,但返回的*Rows必须显式.Close(),否则连接不会归还池中,高并发下很快耗尽连接 - 简单判断标准:有没有要遍历的行?有 →
Query/QueryRow;没有 →Exec
连接池配置不合理引发超时和堆积
默认连接池最大连接数是 0(无限制),最小空闲连接是 2,这在容器化或高并发场景下极易拖垮数据库或自身 OOM。
立即学习“go语言免费学习笔记(深入)”;
- 必须显式设置:
db.SetMaxOpenConns(20)、db.SetMaxIdleConns(10)、db.SetConnMaxLifetime(5 * time.Minute) -
SetMaxOpenConns不是越大越好:超过数据库允许的最大连接数会直接拒绝新连接;建议设为 DBA 给出的单实例上限的 70% - 忘记调
db.Close()?程序退出前不关,连接池里的连接不会释放,Linux 下表现为大量TIMED_WAITsocket 积压
最常被忽略的是:连接池参数要在 sql.Open 之后、首次使用之前设置,晚了就无效。还有,context 超时控制得加在每条 Query/Exec 上,光靠连接池超时不够。










