queryrow卡住因未调用scan致连接不释放;sqlx插入选insert(字段严格对应)或namedexec(需跳过字段/自定义sql);mysql存time需dsn加parsetime=true;defer tx.rollback()不生效因panic被recover或提前return,应显式控制。

为什么 database/sql 的 QueryRow 会卡住不返回?
因为没调用 Scan,底层连接就一直被占着,后续查询可能直接超时或阻塞。这不是 bug,是 Go 的显式资源管理设计——QueryRow 返回的 *Row 必须消费,否则连接不会归还给连接池。
- 常见错误现象:
QueryRow后直接 return,没Scan,接着下一次QueryRow就 hang 住(尤其在连接数少、并发稍高时) - 正确做法:哪怕只查一个字段,也得
err := row.Scan(&id);如果不确定是否有结果,用if err == sql.ErrNoRows判断 - 别用
row.Columns()或row.Err()替代Scan——它们不释放连接 - 性能影响:漏掉
Scan会导致连接池快速耗尽,max_open_conns设为 10 的话,10 个漏扫就全卡死
用 sqlx 做结构体批量插入时,Insert 和 NamedExec 怎么选?
sqlx.Insert 是语法糖,本质还是拼 SQL;NamedExec 更灵活,但得自己写完整语句。选哪个取决于你是否信任字段顺序、要不要复用 struct tag。
- 场景一:表字段和 struct 字段严格一一对应,且字段顺序稳定 → 用
sqlx.Insert,省得写 SQL 字符串 - 场景二:要跳过某些字段(比如
created_at由 DB 默认值生成),或字段名和 struct tag 不一致 → 必须用NamedExec配合INSERT INTO t (a,b) VALUES (:a, :b) - 参数差异:
Insert自动从 struct tag(如db:"user_id")推导列名;NamedExec依赖 SQL 中的命名占位符,不读 tag - 兼容性注意:PostgreSQL 的
RETURNING在Insert中支持有限,想取自增 ID 还是老实用NamedExec+QueryRow
time.Time 存进 MySQL 时为什么总是变成 0000-00-00 00:00:00?
不是 Go 的问题,是 DSN 里缺了 parseTime=true,驱动默认把时间当字符串解析,而 MySQL 严格模式下拒绝非法格式。
- 错误现象:Go 里
time.Now()打印正常,存进去就变零值,日志里还看不到报错 - 必须加的 DSN 参数:
?parseTime=true&loc=Local(后者决定时区,按需改UTC) - 别信
time.Unix(0,0)能绕过——它照样被当成无效时间传给 MySQL - 额外坑:
loc=UTC时,如果 DB 服务器时区是 CST,时间值会偏移 8 小时,务必统一时区或全用 UTC 存储
同步脚本里用 defer tx.Rollback() 为什么有时不生效?
因为 defer 只在函数退出时执行,如果事务中途 panic 但被 recover 拦住了,defer 还是会跑;但如果提前 return 且忘了删 defer,就会误 Rollback 成功的事务。
立即学习“go语言免费学习笔记(深入)”;
- 最稳妥写法:用
if err != nil { tx.Rollback(); return err }显式控制,而不是全靠 defer - 常见错误:在
for循环里开事务,每个迭代都 defer,结果只有最后一个 defer 生效(前面的被覆盖) - 性能提示:频繁
Rollback不会拖慢速度,但反复建连+回滚会放大网络开销,不如提前校验数据合法性 - 容易忽略的点:
tx.Commit()成功后,再调tx.Rollback()会 panic —— 所以 defer 前最好加个已提交标记
Scan、哪个 DSN 参数漏了、哪次 defer 没配对。这些地方一错,表现就是“看起来没报错,但数据就是不对”。










