多租户架构的基本概念
在构建SaaS类网络应用时,多租户设计是一个绕不开的话题。比如一个在线CRM系统,需要同时为上百家企业提供服务,每家企业都希望数据独立、界面可定制,又不希望单独部署一套系统。这时候,多租户架构就成了最优解。
简单来说,多租户就是让一个应用实例为多个客户(租户)提供服务,每个租户的数据和配置相互隔离,互不干扰。就像一栋写字楼,每个公司租用不同的楼层,共用电梯和物业,但内部装修和办公规则各自独立。
常见的多租户实现模式
根据数据隔离程度和资源分配方式,多租户架构通常分为三种模式:
独立数据库:每个租户拥有自己的数据库。这种模式隔离性最好,安全性高,适合对数据敏感的客户,比如金融类企业。缺点是运维成本高,数据库连接数容易成为瓶颈。
共享数据库,独立Schema:所有租户共用一个数据库,但每个租户有独立的Schema。这种方式在PostgreSQL中很常见,管理比独立数据库轻松,同时保持较好的隔离性。
共享数据库,共享Schema:所有租户数据存放在同一套表结构中,通过tenant_id字段区分。这是最节省资源的方式,适合租户数量大、单个租户数据量小的场景,比如中小企业的协同办公工具。
代码层面如何支持多租户
在Spring Boot这类主流框架中,可以通过拦截器或过滤器自动识别租户信息。例如,从请求头中提取X-Tenant-ID:
public class TenantFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String tenantId = httpRequest.getHeader("X-Tenant-ID");
if (tenantId != null && !tenantId.isEmpty()) {
TenantContext.setTenantId(tenantId);
}
try {
chain.doFilter(request, response);
} finally {
TenantContext.clear();
}
}
}配合MyBatis或Hibernate的拦截器机制,可以在SQL执行前自动注入tenant_id条件,避免业务代码到处写过滤逻辑。
租户级别的配置与扩展
除了数据隔离,多租户系统还需要支持租户级配置。比如A公司希望启用审批流程,B公司则不需要。可以在数据库中建立配置表:
CREATE TABLE tenant_config (
id BIGINT PRIMARY KEY,
tenant_id VARCHAR(64),
config_key VARCHAR(100),
config_value TEXT,
UNIQUE(tenant_id, config_key)
);启动时加载各租户配置到缓存,运行时根据当前租户动态调整功能开关。前端也可以根据配置返回不同的菜单结构或字段显示规则。
性能与安全的平衡
共享Schema模式下,一个租户的大量数据可能拖慢整个数据库查询。需要在关键查询上强制走tenant_id索引,并定期做租户数据归档。同时,权限校验不能只靠前端控制,每次API调用都必须验证当前租户是否有权访问目标数据。
对于特别大的租户,可以考虑“混合部署”:主体使用共享架构,当数据量或性能需求达到阈值时,自动迁移到独立数据库实例,实现平滑升级。
多租户不是简单的技术选型,而是产品思维的体现。它要求我们在架构初期就想清楚:谁是我们的租户?他们之间的差异有多大?未来是否需要支持私有化部署?这些问题的答案,会直接影响架构的灵活性和维护成本。