Go多租户核心在于数据隔离策略选择与落地:tenant_id字段隔离需DAO层强制注入、schema隔离须为每租户维护独立*gorm.DB实例、数据库隔离要求连接池限流及配置中心动态加载连接串。

Go 语言本身不提供多租户能力,关键在数据隔离策略的选择和落地——选错隔离粒度(如该用 schema 隔离却硬上 tenant_id 字段),后续几乎无法安全回退。
tenant_id 字段隔离适合中小 SaaS,但必须全局强制过滤
这是最轻量的实现方式:所有租户共享同一套表结构,在每张业务表加 tenant_id 字段,并在每个查询中显式带上 WHERE tenant_id = ?。问题在于极易漏写——一次忘记加条件,就导致数据越界。
- 必须封装 DAO 层,所有
FindOne、FindAll、Update方法都要求传入tenantID string,内部自动注入WHERE tenant_id = ? - 禁止直接使用
db.Where(...).Find()这类裸 GORM 调用;可借助 GORM 的Scopes定义一个tenantScope函数,统一挂载 - 软删除字段(如
deleted_at)也要和tenant_id同级约束,否则Unscoped().Where(...)可能绕过租户检查
PostgreSQL schema 隔离更安全,但 Go 连接池要动态切换
每个租户独占一个 database schema(如 tenant_123),物理级隔离。优势是天然防误查,权限可精确到 schema;劣势是连接池不能复用不同 schema 的连接,且 migration 和监控成本上升。
- 不能靠
SET search_path TO tenant_123临时切 schema——GORM 不保证事务内所有语句都走同一连接,可能跨连接失效 - 正确做法是为每个活跃租户维护独立的
*gorm.DB实例,用tenantID作 key 缓存在sync.Map中,首次访问时按需初始化(连接字符串拼接dbname=myapp?options=-c+search_path=tenant_123) - GORM v2 的
Session不支持运行时换 schema,别试图用db.Session(&gorm.Session{NewDB: true})替代
连接串 + 数据库名隔离适用于强合规场景,但部署复杂
每个租户拥有独立数据库实例(如 saas_tenant_456),隔离强度最高,满足金融/医疗等审计要求。代价是数据库数量线性增长,备份、扩缩容、版本同步全都要自动化。
- Go 服务启动时不预连任何 DB,而是在 HTTP middleware 中解析请求头或子域名(如
acme.example.com→tenant_acme),再从配置中心拉取对应连接串 - 连接池必须限制最大数(如
SetMaxOpenConns(5)),否则 1000 个租户 × 默认 100 连接 = 10 万连接,直接打崩 PostgreSQL - 不要把数据库名写死在代码里,配置项应为
tenant_db_mapJSON 字符串,由运维热更新,避免重启服务
真正难的不是“怎么写代码”,而是租户生命周期和数据迁移路径——比如合并两个租户时,schema 隔离要导出再导入,字段隔离则要批量改 tenant_id 值并校验外键一致性。这些操作没有现成 Go 库,得自己写带事务回滚和断点续传的迁移工具。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











