基于PostgreSQL RLS的多租户SaaS数据库设计方案咨询(适配Supabase)
方案合理性评估
你初步设计的user_data_for_tenant表结构是合理的,完全适配你当前的业务场景:
- 匹配共享用户+租户专属数据的隔离诉求:核心用户公共数据存储在用户主表,租户侧的专属扩展数据独立存储,不会产生数据冗余
- JSONB存储自定义字段的方案适配「租户前端自定义配置」的需求,不需要频繁修改表结构,PostgreSQL对JSONB的索引、查询支持成熟,Supabase也完全兼容JSONB的各类操作
- 配合RLS做权限控制的成本极低:只需要给该表加RLS策略,关联
permissions表校验当前租户是否拥有该用户的访问权限,同时保证只有对应tenant_id的租户才能操作自有专属数据
你可以在现有设计基础上做如下优化:
- 新增
user_id+tenant_id联合唯一约束,避免同一个用户在同一个租户下出现多条冗余的专属数据记录 - 给JSONB字段配置适配的索引:如果租户会频繁查询
data内的固定字段(比如加入组织日期),可以给对应路径加GIN索引或者表达式索引,提升查询效率 - 新增
schema_version字段:如果租户的自定义配置会迭代升级,版本号字段可支撑后续数据兼容迁移 - 敏感数据加密:如果
data内包含租户的敏感自定义数据,可在写入层做加密后存储,降低数据泄露风险
其他适配场景的可选设计方案
根据你业务的后续发展方向,还有两种可选方案可以参考:
方案1:预定义扩展字段+预留动态字段
如果租户自定义的字段类型大部分是固定的(比如日期、文本、数字三类),可以不用纯JSONB,改成混合结构:
user_data_for_tenant id user_id tenant_id join_date TIMESTAMP -- 通用高频字段预先定义 custom_text1 TEXT custom_text2 TEXT custom_num1 INT custom_num2 INT custom_date1 TIMESTAMP extra JSONB -- 极特殊的自定义配置存储在此处
该方案优点是预定义字段的查询、排序性能高于JSONB,也方便实现统计类需求,适合租户自定义字段规律性较强的场景。
方案2:租户级自定义元数据表+属性值表
如果需要支持租户对自定义字段做更复杂的配置(比如字段类型、校验规则、是否必填、展示顺序),可以拆成两张表:
第一张存储租户定义的自定义字段元数据:
tenant_user_custom_fields id tenant_id field_name VARCHAR field_type VARCHAR -- 可选值为text/number/date等 is_required BOOLEAN sort_order INT
第二张存储对应的属性值:
user_data_for_tenant id user_id tenant_id field_id INT -- 关联tenant_user_custom_fields表的id value TEXT -- 存储实际值,使用时按field_type做类型转换
该方案优点是租户可以完全自主管理自定义字段的配置,适合SaaS平台对租户开放度很高、需要支持复杂自定义表单的场景,缺点是查询用户所有属性需要关联表,复杂度高于单表JSONB结构。
Supabase 适配建议
你使用Supabase的话,可以直接配合内置能力简化实现:
- RLS策略可直接关联
auth.uid()与租户ID,同时关联permissions表校验租户是否有该用户的访问权限,示例策略如下:
CREATE POLICY "租户只能访问自己的用户专属数据" ON user_data_for_tenant FOR ALL USING ( tenant_id = current_setting('app.current_tenant_id')::UUID AND EXISTS ( SELECT 1 FROM permissions WHERE permissions.user_id = user_data_for_tenant.user_id AND permissions.tenant_id = user_data_for_tenant.tenant_id AND permissions.status = 'approved' ) );
- 可配合Supabase的行级安全和PG的CHECK约束,给JSONB字段加租户侧的格式校验,避免非法数据写入。
内容的提问来源于stack exchange,提问作者Ghost
相关产品推荐
相关产品推荐

