首页 / 帮助文档 / 分布式数据库多租户数据隔离实现方式

分布式数据库多租户数据隔离实现方式

分布式数据库的多租户数据隔离,本质上是在一个物理集群里划出多个逻辑边界,让不同租户的数据彼此不可见、不可干扰。业界主流的实现方式可以归纳为三种:基于Schema的逻辑隔离、基于数据分区的物理隔离,以及基于行级标签的混合隔离。这三种方案没有绝对优劣,完全取决于你的业务规模、合规要求和运维成本承受能力。

先看最轻量的方案——Schema隔离。在TiDB、OceanBase、CockroachDB这类NewSQL数据库里,每个租户对应一个独立的Database或Schema,所有租户共享同一套计算和存储节点。租户A查询自己的订单表,实际访问的是tenant_a.orders,租户B则访问tenant_b.orders。数据库自身的权限体系确保租户A永远无法跨Schema访问。这种方案的优势极其明显:运维成本低,一个集群就能承载上千个中小租户,扩容时所有租户均摊硬件成本。但问题也很直接——资源争抢。某个租户突发大查询,可能拖慢整个集群的响应速度。要缓解这一点,通常需要配合资源隔离机制,比如TiDB的Resource Control或OceanBase的资源单元,给每个租户的SQL设置CPU和IO上限。

当业务对隔离性要求更高时,物理隔离就派上用场了。物理隔离的核心思路是把不同租户的数据分布到不同的物理节点或数据分区上。在分布式数据库里,这通常通过分区策略实现。以TiDB为例,你可以用List分区把不同租户的数据强制落在不同的TiKV Region上,再结合Placement Rules把特定Region绑定到特定物理节点。比如租户A的所有表都带一个tenant_id字段,建表时按tenant_id做List分区,然后通过Placement Policy把分区P_A的数据副本限定在节点组NodeGroup1上,租户B的分区P_B则限定在NodeGroup2上。这样一来,两个租户的数据在磁盘和内存层面彻底隔离,即使一个租户的数据节点宕机,也不会影响另一个租户。代价是资源利用率会下降,因为每个节点组都要独立预留冗余容量。

-- 按租户ID创建List分区表
CREATE TABLE orders (
    order_id BIGINT,
    tenant_id INT,
    amount DECIMAL(10,2),
    PRIMARY KEY (order_id, tenant_id)
) PARTITION BY LIST (tenant_id) (
    PARTITION p_tenant_a VALUES IN (1001),
    PARTITION p_tenant_b VALUES IN (1002)
);

-- 通过Placement Rules绑定分区到特定节点
ALTER PARTITION p_tenant_a OF TABLE orders 
    PLACEMENT POLICY = 'tenant_a_policy';

物理隔离还有一种更彻底的形态——实例级隔离,也就是在同一个分布式数据库平台上为每个大租户创建独立的数据库实例。OceanBase的多租户架构在这方面做得比较极致,它的“租户”概念本身就是一组独立的资源单元,拥有独立的内存、线程池和日志流。一个大租户就是一个OceanBase租户,内部还可以再建多个Database给子业务用。这种方案隔离性最强,适合金融、政务等强合规场景,但管理复杂度也最高,实例数量一多,版本升级、备份恢复、监控告警都得逐个处理。

第三种方案是行级标签隔离,它介于逻辑和物理之间,灵活性最高。做法是在每张业务表里都嵌入一个tenant_id列,应用层在每条SQL里强制带上租户过滤条件。数据库层面可以配合行级安全策略自动注入这个条件,避免开发人员遗漏。PostgreSQL的Row Level Security就是典型实现,CockroachDB和YugabyteDB也支持类似机制。开启RLS后,租户A执行SELECT * FROM orders时,数据库自动改写为SELECT * FROM orders WHERE tenant_id = current_tenant_id(),这个current_tenant_id来自会话上下文,应用在建立连接时设置好。行级标签隔离的好处是数据分布均匀,不会出现热点分区,扩容缩容都很平滑。但它的隔离强度依赖应用层和数据库策略的正确配置,一旦某处代码漏了租户过滤,就可能造成数据泄露。

-- 开启行级安全策略
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;

-- 创建策略:租户只能看到自己的数据
CREATE POLICY tenant_isolation ON orders
    FOR ALL
    TO tenant_user
    USING (tenant_id = current_setting('app.current_tenant_id')::INT);

实际生产环境中,很少只用单一方案,大多是组合拳。一个典型的SaaS系统可以这样分层:对于免费试用的小租户,统一放在共享Schema里,靠资源管控限制影响范围;对于付费的中型租户,在共享集群里用分区物理隔离,保证性能稳定性;对于头部大客户,直接给独立实例,数据物理隔离且可定制运维策略。这种分层架构的关键在于元数据管理——你需要一个全局的租户路由表,记录每个租户的隔离级别、所在集群、连接串等信息,应用启动时根据租户ID动态路由到对应的数据源。

多租户隔离还有一个容易被忽略的维度:元数据和索引的隔离。很多团队只关注业务数据,却忘了数据库的统计信息、执行计划、全文索引也可能跨租户泄露信息。比如两个租户共用一张表的不同分区,全局索引里可能包含所有租户的键值。虽然数据库不会直接返回其他租户的数据,但通过执行计划的索引扫描范围、查询耗时差异,理论上可以侧信道推断出其他租户的数据分布。对安全要求极高的场景,建议关闭全局索引,改用本地索引,或者干脆把不同租户的表完全独立,不共享任何物理结构。

运维层面的隔离同样重要。备份恢复、数据导出、慢查询日志这些操作都必须带上租户维度。一个常见的坑是DBA执行全表扫描排查问题时,无意中看到了所有租户的数据。正确的做法是建立租户化的运维账号体系,DBA日常使用受限账号,只能查看元数据而不能接触业务数据,紧急情况下才通过审批流程获取临时权限。分布式数据库的审计日志也要记录每次数据访问的租户上下文,这样才能在事后追溯。

从技术选型角度看,如果你的租户数量在百级以下、单个租户数据量不大,Schema隔离配合资源管控是最经济的选择。当租户数量增长到千级,或者出现数据量超过TB的大租户时,就要考虑引入分区物理隔离,把大租户拆出去。如果业务本身是金融、医疗等强监管行业,一开始就应该采用实例级或分区级物理隔离,不要等合规审计发现问题再改造,那时候数据迁移的成本会非常高。行级标签隔离适合那些租户数量巨大但每个租户数据量很小的场景,比如IoT平台、多租户CRM,它的弹性最好,但对研发规范的要求也最高。

还有一个趋势值得关注:Serverless形态的分布式数据库正在改变多租户隔离的实现方式。像TiDB Serverless、PlanetScale这类产品,底层自动把不同租户的数据分散到共享存储的不同分片上,通过多租户调度器动态分配计算资源。用户不需要关心分区策略和资源管控参数,平台根据实际负载自动调整。这种方案把隔离的复杂度从用户侧转移到了数据库内核,对中小团队来说是个省心的选择,但大企业可能会顾虑数据物理位置不透明、故障域不可控等问题。

总结一下,分布式数据库的多租户隔离没有银弹,选择哪种方案取决于你对安全隔离、资源利用率、运维成本三者的权衡。Schema隔离成本最低但隔离性最弱,物理隔离最强但成本最高,行级标签隔离最灵活但对研发要求高。实际落地时,建议先用Schema隔离快速上线,再根据租户分级逐步引入物理隔离,同时用行级安全策略作为最后一道防线,形成纵深防御。