Go数据库错误需用驱动特定方式判断:PostgreSQL用pq.Error.SQLState()(如"08006"连拒、"57014"超时),MySQL用mysql.MySQLError.Number(如2003、1317)或net.OpError;sql.ErrNoRows仅适用于QueryRow.Scan,Query需靠Next()返回false;事务必须显式Rollback,推荐封装函数统一错误处理。

Go 的数据库错误不是统一类型,必须用具体驱动的错误判断逻辑,不能只靠 err != nil 就认为操作失败。
如何判断是连接失败还是查询超时
PostgreSQL(lib/pq)和 MySQL(go-sql-driver/mysql)都提供可识别的错误类型,但方式不同。直接用 errors.Is 或类型断言才能区分真实原因。
- 连接拒绝(如端口错、服务未启动):PostgreSQL 返回
*pq.Error,SQLState()为"08006";MySQL 返回*mysql.MySQLError,Number为1042或2003 - 查询超时:PostgreSQL 中常见
SQLState() == "57014"(canceling statement due to user request,常由context.WithTimeout触发);MySQL 中Number == 1317(query execution was interrupted)或底层net.OpError(如timeout: read tcp ...: i/o timeout) - 永远不要只写
if err != nil { log.Fatal(err) }—— 这会把连接失败和主键冲突当成同级错误处理
为什么不能用 errors.Is(err, sql.ErrNoRows) 判断所有“查不到”场景
sql.ErrNoRows 只在 *sql.Row.Scan() 时返回,而 *sql.Rows.Next() 遇到空结果集不会触发该错误,它只是循环不执行。混淆这两者会导致逻辑跳过本应处理的空数据分支。
-
db.QueryRow(...).Scan(&v):查无结果 → 返回sql.ErrNoRows -
rows, _ := db.Query(...); for rows.Next() { ... }:查无结果 →rows.Next()直接返回false,err为nil - 如果业务要求“必须有且仅有一行”,用
QueryRow+Scan;如果接受零或多行,用Query+Next,别指望sql.ErrNoRows出现在那里
事务中错误没回滚就 return 的坑
Go 没有 defer tx.Rollback() 的自动作用域管理,一旦在 tx.Query 后 return 却忘了 tx.Rollback(),连接会卡在事务状态,后续复用该连接可能报 "pq: current transaction is aborted"(PostgreSQL)或被 MySQL 杀掉。
立即学习“go语言免费学习笔记(深入)”;
- 正确模式:用
defer+ 标志位控制是否已提交:tx, _ := db.Begin() defer func() { if r := recover(); r != nil || !committed { tx.Rollback() } }() // ... 执行语句 tx.Commit() committed = true - 更稳妥做法:把事务逻辑封装进函数,用
return err统一出口,再在外层判断是否回滚 - 注意
tx.Commit()本身也可能返回 error(如 PostgreSQL 的 prepared statement 冲突),不能忽略它的返回值
最易被忽略的是驱动特定错误码的稳定性——比如 lib/pq 的 SQLState 是标准定义,但某些 MySQL 驱动自定义错误码可能随版本变动。线上关键路径建议用 errors.As 抽取驱动错误结构体,而不是硬比对数字或字符串。










