Beego 连 PostgreSQL 需注册 lib/pq 驱动、固定格式连接串、SET search_path 实现 schema 级多租户隔离;Filter 无效于 Raw/Unscoped,须用 sync.Map 缓存租户 DB 实例并限连池。

Beego 连 PostgreSQL 不是“配个连接串就能用”,关键在驱动注册、search_path 绑定、以及避开 ORM 的全局过滤幻觉——尤其当你打算做多租户时,Filter("tenant_id", tid) 根本拦不住 Raw() 和 Unscoped()。
PostgreSQL 驱动和连接串必须配对正确
Beego 的 orm.RegisterDriver 和 orm.RegisterDataBase 必须严格对应:postgres 驱动名 + lib/pq 实际驱动包。漏掉下划线导入或写错驱动名(比如写成 postgresql 或 pg),运行时会报 driver: unknown driver "postgres" (forgotten import?)。
-
import _ "github.com/lib/pq"必须存在,且不能写成"github.com/lib/pq"(没下划线) - 连接串格式固定为
user=xxx password=xxx dbname=xxx host=xxx port=xxx sslmode=xxx,不支持?查询参数写法(那是 MySQL 风格) -
sslmode=disable在本地开发可接受;生产环境若启用了 SSL,需同步配置sslcert/sslkey路径
多租户场景下别碰字段级隔离
Beego 的 Filter("tenant_id", tid) 是 ORM 层的软过滤,所有绕过 ORM 抽象的操作都会失效:比如 o.Raw("SELECT * FROM users")、o.QueryTable("users").Unscoped().All(...)、甚至 Joins() 关联查询都可能跨租户读取数据。
- 真正安全的做法是每个租户一个 PostgreSQL schema(如
tenant_abc),初始化连接后执行db.Exec("SET search_path TO tenant_abc") -
QueryTable("users")会自动落在当前search_path下的users表,无需改写表名 - 必须用
sync.Map缓存tenant_id → *sql.DB映射,否则每次请求新建连接,1000 租户 × 默认 100 连接 = PG 直接 OOM
连接池参数不设限等于自毁
PostgreSQL schema 隔离依赖独立 *sql.DB 实例,但每个实例默认 MaxOpenConns = 0(无上限),这在租户数增长时极其危险。
- 每个租户专属的
*sql.DB必须显式调用SetMaxOpenConns(5)(根据实际负载调整,通常 3–10 足够) - 别指望 Beego 的
orm.RunSyncdb自动处理租户库建表——它只作用于注册时的"default"数据库别名;租户 schema 需提前用 SQL 建好,或在首次连接时手动执行CREATE SCHEMA IF NOT EXISTS tenant_abc -
orm.RegisterDataBase只能静态注册,无法动态增删租户库;租户 DB 实例必须由业务代码按需创建并管理生命周期
PostgreSQL schema 隔离看着简单,但 search_path 绑定时机、连接复用策略、租户上下文传递路径,三者缺一不可;少做一步,就可能在某个 Raw() 调用里悄悄泄露全量数据。


















