Beego ORM 必须注册名为 default 的数据库别名,否则调用 orm.RunSyncdb() 或 orm.NewOrm() 时会因找不到 default 别名而 panic 崩溃;所有数据库连接必须显式命名唯一别名(如 "default"、"log_db"),且 RegisterDataBase 调用须在 ORM 初始化前完成。

Beego 中 RegisterDataBase 支持多数据库注册,但必须显式命名
Beego ORM 不允许存在多个未命名的数据库连接。你必须为每个数据库指定一个唯一别名(如 "default"、"log_db"、"tenant_a"),否则后续调用会覆盖前一个。别名会在 QueryTable、Using 等操作中被引用。
常见错误是只注册一个 "default",然后在代码里试图“动态切换”——Beego 没有运行时切换默认库的机制,所有操作都基于初始化时注册的别名。
-
orm.RegisterDataBase("default", "mysql", dsn1)和orm.RegisterDataBase("backup", "mysql", dsn2)是合法且必要的写法 - 若漏掉第二个参数(driverName),或 driverName 与 DSN 类型不匹配(比如用
"postgres"驱动连 MySQL),启动时会 panic,错误信息类似"driver: unknown driver \"postgres\" (forgotten import?) - 别名不能含空格或特殊字符;建议全小写 + 下划线,例如
"analytics_db"
跨数据库查询必须手动指定 Using,QueryTable 默认走 "default"
Beego 的 QueryTable("user") 永远查的是 "default" 库里的 user 表,不会自动识别当前请求该用哪个库。你要显式调用 .Using("log_db") 才能切到对应连接。
典型误用:在日志写入逻辑里忘了 .Using("log_db"),结果日志写进了业务库,污染主数据。
- 正确写法:
o := orm.NewOrm().Using("log_db"); o.Insert(&LogEntry{...}) - DAO 层建议封装方法,如
SaveToLogDB(entry *LogEntry) error,内部固定调用.Using("log_db"),避免每次手写 -
Raw()同样受Using影响,但注意:它不解析表名前缀,o.Using("tenant_a").Raw("SELECT * FROM users")查的是tenant_a库下的users表(MySQL)或tenant_a.users(PostgreSQL schema 模式下需配合search_path)
PostgreSQL 多租户慎用“多 database”,优先走 search_path + 单连接池
MySQL 下“一个租户一个 database”尚可勉强维护;PostgreSQL 下硬套这条路等于放弃 Beego ORM 优势。因为 PostgreSQL 的 database 是重量级隔离单位,无法像 schema 那样轻量切换。
真正可行的是:共用一个物理 database,每个租户分配独立 schema(如 tenant_abc),连接初始化时执行 SET search_path TO tenant_abc,后续所有 QueryTable("users") 自动落在该 schema。
- 必须用
sync.Map缓存租户 ID →*sql.DB映射,避免每请求新建连接 - 每个租户专属的
*sql.DB实例必须调用SetMaxOpenConns(5),否则 1000 租户 × 默认 100 连接 = 10 万连接,PG 直接 OOM - 不要在 Beego 初始化阶段一次性注册全部租户库(如
RegisterDataBase("tenant_001", ...)),那是配置爆炸,不可运维
MySQL 多租户只能靠字段隔离 + 数据库层 RLS,Beego ORM 本身不兜底
MySQL 没有 search_path,也没有原生 schema 权限粒度。你在 Beego 里加 Filter("tenant_id", tid),对 Raw()、Joins()、Unscoped() 全无效——这些方法直接拼 SQL,绕过所有全局过滤。
测试也难暴露问题:单元测试常 mock DB 层,真实 SQL 漏条件根本跑不出来。线上一旦漏写 Filter 或误用 Unscoped(),就跨租户读写。
- 唯一勉强可行路径:MySQL 8.0+ 开启 ROW ACCESS POLICY,在数据库层强制拦截非本租户访问,Beego 层仍要严格审计所有 DAO 方法
- 禁止把租户 ID 当作普通参数传入
Raw(),应始终用预处理占位符,防止注入同时确保条件生效 - 字段隔离方案下,务必禁用
Unscoped(),或重写其行为(需 patch Beego 源码,不推荐)
多数据库不是开关一开就自动分流,每个别名都是独立连接池、独立事务上下文、独立权限边界。最容易被忽略的是:Beego 不做跨库 JOIN,也不做连接复用,租户数上来后,连接数、内存、CPU 会呈线性甚至指数增长——这必须从架构初期就用 sync.Map + 显式限流卡死。


















