PostgreSQL 在国内不如 MySQL 普及,但在技术圈口碑极好。JSON 支持、数组类型、全文搜索、地理信息扩展,这些高级特性让它在复杂业务场景下表现亮眼。
GORM 的 PostgreSQL 驱动:
go get -u gorm.io/driver/postgres
底层依赖 github.com/lib/pq,这是 Go 社区最成熟的 PostgreSQL 驱动。
package main
import (
"fmt"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
func main() {
dsn := "host=localhost user=gorm password=gorm dbname=gorm port=5432 sslmode=disable"
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
panic("连接失败")
}
fmt.Println("连接成功")
}
PostgreSQL 的 DSN 有两种格式。
键值对格式
host=localhost port=5432 user=gorm password=gorm dbname=gorm sslmode=disable
每个参数用空格分隔,可读性好。参数顺序无关。
URI 格式
postgres://gorm:gorm@localhost:5432/gorm?sslmode=disable
类似 MySQL 的格式,习惯 URL 风格的可以用这个。
| 参数 | 说明 | 默认值 |
|---|---|---|
| host | 数据库地址 | localhost |
| port | 端口 | 5432 |
| user | 用户名 | 环境变量 USER |
| password | 密码 | 空 |
| dbname | 数据库名 | 用户名 |
| sslmode | SSL 模式 | disable |
| TimeZone | 时区 | UTC |
| connect_timeout | 连接超时秒数 | 0(无限制) |
sslmode 选项
disable - 不使用 SSLrequire - 必须使用 SSL,但不验证证书verify-ca - 使用 SSL 并验证证书verify-full - 使用 SSL 并验证证书和主机名生产环境建议至少用 require。
postgres.Config 提供专属配置:
import "gorm.io/driver/postgres"
pgConfig := postgres.Config{
DSN: dsn,
PreferSimpleProtocol: true,
}
db, err := gorm.Open(&pgConfig, &gorm.Config{})
PreferSimpleProtocol
PostgreSQL 默认使用 prepared statement 协议。某些场景下(比如 PgBouncer 连接池)会有问题,设为 true 改用简单协议。
PostgreSQL 有 Schema 的概念,类似命名空间。默认连接到 public schema:
dsn := "host=localhost user=gorm password=gorm dbname=gorm search_path=public"
指定其他 schema:
dsn := "host=localhost user=gorm password=gorm dbname=gorm search_path=myschema"
或者连接后动态切换:
db.Exec("SET search_path = myschema")
PostgreSQL 存储时间时会保留时区信息。DSN 中指定时区:
dsn := "host=localhost user=gorm password=gorm dbname=gorm TimeZone=Asia/Shanghai"
Go 代码中处理时间:
type Event struct {
ID uint
CreatedAt time.Time
}
event := Event{}
db.First(&event)
fmt.Println(event.CreatedAt.Local())
PostgreSQL 原生支持 JSON 和 JSONB,GORM 可以直接映射:
type User struct {
ID uint
Profile datatypes.JSON
}
user := User{
Profile: datatypes.JSON(`{"name": "张三", "age": 25}`),
}
db.Create(&user)
需要导入:
import "gorm.io/datatypes"
查询 JSON 字段:
var user User
db.Where("profile->>'name' = ?", "张三").First(&user)
JSONB 支持索引,查询性能更好,推荐使用:
type User struct {
ID uint
Profile datatypes.JSON `gorm:"type:jsonb"`
}
PostgreSQL 支持数组,GORM 通过 lib/pq 支持:
import "github.com/lib/pq"
type Article struct {
ID uint
Tags pq.StringArray `gorm:"type:text[]"`
}
article := Article{
Tags: pq.StringArray{"Go", "GORM", "PostgreSQL"},
}
db.Create(&article)
查询数组元素:
db.Where("tags @> ARRAY[?]::text[]", "Go").Find(&articles)
PostgreSQL 使用序列(Sequence)实现自增。GORM 默认使用 SERIAL 类型:
type User struct {
ID uint `gorm:"primaryKey"`
Name string
}
生成的 SQL:
CREATE TABLE "users" ("id" SERIAL,"name" text)
手动指定序列名:
type User struct {
ID uint `gorm:"primaryKey;autoIncrement;sequence:user_id_seq"`
}
PostgreSQL 原生支持 UUID,非常适合分布式系统:
import "github.com/google/uuid"
type User struct {
ID uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()"`
Name string
}
需要先启用扩展:
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
或者用 gen_random_uuid()(PostgreSQL 13+):
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
和 MySQL 一样,需要配置连接池:
sqlDB, _ := db.DB()
sqlDB.SetMaxOpenConns(50)
sqlDB.SetMaxIdleConns(10)
sqlDB.SetConnMaxLifetime(time.Hour)
PostgreSQL 默认 max_connections 是 100,连接池配置要留余量。
import (
"database/sql"
"gorm.io/driver/postgres"
)
func ConnectFromExisting(sqlDB *sql.DB) (*gorm.DB, error) {
return gorm.Open(postgres.New(postgres.Config{
Conn: sqlDB,
}), &gorm.Config{})
}
角色不存在
ERROR: role "gorm" does not exist
先创建角色:
CREATE ROLE gorm WITH LOGIN PASSWORD 'gorm';
数据库不存在
ERROR: database "gorm" does not exist
创建数据库:
CREATE DATABASE gorm OWNER gorm;
SSL 连接失败
ERROR: no pg_hba.conf entry for host
检查 pg_hba.conf 配置,允许对应主机的连接。开发环境可以临时改 sslmode=disable。
扩展不存在
ERROR: could not open extension control file
UUID 等扩展需要先安装:
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
从 MySQL 迁移到 PostgreSQL,注意这些差异:
| 特性 | MySQL | PostgreSQL |
|---|---|---|
| 字符串引号 | 单引号或双引号 | 只能用单引号 |
| 标识符引号 | 反引号 ` | 双引号 " |
| 布尔类型 | TINYINT(1) | BOOLEAN |
| 自增 | AUTO_INCREMENT | SERIAL/IDENTITY |
| 字符串连接 | CONCAT() | || |
| 限制结果 | LIMIT | LIMIT + OFFSET |
GORM 会处理大部分差异,但写原生 SQL 时要注意。
PostgreSQL 功能强大,但配置细节和 MySQL 有差异。JSON、数组、UUID 这些特性用好了能简化很多业务逻辑。下一章看轻量级的 SQLite。