用 database/sql + pgx 驱动连 PostgreSQL 最稳:需导入 _ "github.com/jackc/pgx/v5/database/sql",用 URL 连接串(含 sslmode=disable),Scan 严格按字段顺序,时间/数值用 *time.Time/sql.NullInt64,事务用 defer tx.Rollback() 并显式 Commit,批量插入优先 pgx.Batch。

用 database/sql + pgx 驱动连 PostgreSQL 最稳
Go 官方 database/sql 接口本身不实现数据库协议,必须配驱动。别用过时的 lib/pq(已归档),直接上 pgx ——它兼容 database/sql,同时支持原生协议(性能高、类型映射准),还能开连接池、跑批量操作。
常见错误现象:sql.Open("postgres", "...") 报 driver: unknown driver "postgres",就是没导入驱动;或者连上了但 time.Time 字段读出来是空值,那是类型映射没对齐。
- 安装:
go get github.com/jackc/pgx/v5(v5 是当前主流) - 导入驱动:
import _ "github.com/jackc/pgx/v5/database/sql"(注意下划线) - 连接字符串建议用 URL 格式:
postgres://user:pass@localhost:5432/dbname?sslmode=disable,sslmode=disable在本地开发时必须显式写上,否则默认尝试 SSL 连不上 -
pgx的database/sql适配层不支持pgx.Conn独有的高级功能(如流式查询、自定义类型注册),真要用得切回原生pgxAPI
Scan 时字段顺序错位或类型不匹配就 panic
Go 没有运行时反射字段名绑定,rows.Scan() 完全依赖 SQL 查询字段顺序和变量顺序严格一致。PostgreSQL 别名、函数表达式、NULL 值都容易踩坑。
使用场景:查用户列表带计算字段,比如 SELECT id, name, NOW() - created_at AS age,结果扫进 struct 时若没按 SELECT 顺序声明字段,就会错位赋值。
立即学习“go语言免费学习笔记(深入)”;
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- 永远用
rows.Columns()检查列名和数量,尤其在动态 SQL 或视图查询时 - 避免裸
Scan(&a, &b, &c),优先封装成 struct 并用sqlx(需额外引入)或手动按序映射 - 时间字段推荐用
*time.Time而非time.Time,防NULL导致 panic;数值字段同理,用sql.NullInt64等 - PostgreSQL 的
jsonb字段不能直接扫进string,会报cannot scan into *string from jsonb,得用json.RawMessage或自定义Scan方法
事务里 exec 失败没 rollback 就泄露连接
Go 的 sql.Tx 不是上下文自动管理的,defer tx.Rollback() 放错位置、或成功后忘了 tx.Commit(),都会让连接卡在事务中,最终耗尽连接池。
性能影响:PostgreSQL 连接池满后新请求会阻塞,超时抛 context deadline exceeded;更隐蔽的是长事务导致表膨胀、锁堆积。
- 标准写法是:先
tx, err := db.BeginTx(ctx, nil),出错立即return err;成功路径末尾return tx.Commit();所有其他分支统一defer tx.Rollback() - 别在事务里调
db.QueryRow(),那走的是新连接,不在事务上下文中 - 如果事务里要查刚 insert 的记录,必须用
tx.QueryRow(),且确保该语句实际执行(比如加.Scan()),否则连接不会释放 -
pgx原生事务支持Savepoint,但database/sql层不暴露,需要降级用pgx.Conn
批量插入用 pgx.Batch 比循环 Exec 快 10 倍以上
单条 INSERT 循环执行本质是 N 次网络往返,而 PostgreSQL 支持一次传多条语句。官方 database/sql 不支持批量,必须用 pgx 原生 Batch 或 CopyFrom。
兼容性注意:Batch 是客户端拼语句发过去,适合中小批量(几千条);CopyFrom 走 PostgreSQL 的二进制 COPY 协议,更快更省内存,但要求表结构完全匹配,且不支持 RETURNING。
- Batch 示例:
b := &pgx.Batch{}; b.Queue("INSERT INTO users(name) VALUES($1)", "alice"); ...; br := conn.SendBatch(ctx, b); for i := 0; i - 别把 Batch 当事务用——它内部不自动开启事务,出错也不会自动回滚,得自己包一层
Begin() - 每批别超过 1000 条,太大可能触发 PostgreSQL 单包限制或内存溢出;太小(如每次 10 条)则失去批量意义
- 如果字段含
DEFAULT或GENERATED,Batch 里不能省略占位符,否则报too few arguments
最常被忽略的是连接池配置:db.SetMaxOpenConns(20) 和 db.SetMaxIdleConns(10) 不设的话,默认是 0(无上限),线上可能把 DB 打挂;还有 pgx 的 ConnConfig.PreferSimpleProtocol = true 在只读场景能省点解析开销,但多数人根本不知道这开关存在。

















