欢迎来到思维库

思维库

揭秘!多租户 SaaS 系统这样设计:数据库级/表级隔离 + 资源配额全攻略

时间:2025-11-05 07:27:40 出处:人工智能阅读(143)

在参与多个大型 SaaS 平台的揭秘架构设计之后,我逐渐发现,多租多租户架构的统样核心价值并不只是“共享”,而是设计数据“隔离 + 配额”。 一方面,库级我们必须确保不同租户之间的表级数据严格分离,以满足合规性与安全性;另一方面,隔离还需要限制资源消耗,资源避免某些租户“独占”系统性能。配额

本文将结合实践经验,全攻全面拆解 多租户 SaaS 系统的揭秘数据隔离方案(数据库级 / 表级 / 行级)与资源配额控制策略,并给出核心代码示例,多租帮助你在实际项目中快速落地。统样

揭秘!多租户 SaaS 系统这样设计:数据库级/表级隔离 + 资源配额全攻略

什么是设计数据多租户架构?

多租户(Multi-Tenancy)是一种典型的 SaaS 模式:

单实例运行:一套系统为多个租户(Tenant)服务。逻辑隔离:每个租户拥有独立的库级业务空间,但共享基础设施(数据库、存储、计算资源)。

多租户架构需要解决两个关键问题:

数据隔离 —— 确保租户之间互不干扰。b2b供应网资源配额 —— 控制存储、API 调用、并发用户数等,防止“资源抢占”。数据隔离方案对比与实现

在多租户架构下,数据隔离常见有三种方式:数据库级、表级和行级。

数据库级隔离

架构思路:每个租户独立一个数据库。

复制+-------------------+ +-------------------+ +-------------------+ | Tenant A Database | | Tenant B Database | | Tenant N Database | +-------------------+ +-------------------+ +-------------------+ | Users Table | | Users Table | | Users Table | | Orders Table | | Orders Table | | Orders Table | +-------------------+ +-------------------+ +-------------------+1.2.3.4.5.6.

代码实现:动态数据源路由

复制// 文件路径: src/main/java/com/icoderoad/tenant/TenantRoutingDataSource.java public class TenantRoutingDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return TenantContextHolder.getTenantId(); // 基于 ThreadLocal 获取租户ID } } // 文件路径: src/main/java/com/icoderoad/tenant/TenantContextHolder.java public class TenantContextHolder { private static final ThreadLocal<String> CONTEXT = new ThreadLocal<>(); public static void setTenantId(String tenantId) { CONTEXT.set(tenantId); } public static String getTenantId() { return CONTEXT.get(); } public static void clear() { CONTEXT.remove(); } }1.2.3.4.5.6.7.8.9.10.11.12.13.14. 表级隔离

架构思路:所有租户共享数据库,但每个租户有独立的表(加前缀)。

复制+---------------------+ | Shared Database | +---------------------+ | TenantA_Users_Table | | TenantA_Orders_Table| | TenantB_Users_Table | | TenantB_Orders_Table| +---------------------+1.2.3.4.5.6.7.8.

代码实现:动态表名拦截器(MyBatis)

复制// 文件路径: src/main/java/com/icoderoad/tenant/TableNameInterceptor.java @Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}) }) public class TableNameInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { StatementHandler handler = (StatementHandler) invocation.getTarget(); BoundSql boundSql = handler.getBoundSql(); String tenantId = TenantContextHolder.getTenantId(); String modifiedSql = boundSql.getSql().replaceAll("\\b(user|order)\\b", tenantId + "_$1"); Field field = boundSql.getClass().getDeclaredField("sql"); field.setAccessible(true); field.set(boundSql, modifiedSql); return invocation.proceed(); } }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17. 行级隔离

架构思路:单库单表,通过 tenant_id 字段区分租户。

复制+-------------------+ | Shared Database | +-------------------+ | Users Table | tenant_id + user_id | Orders Table | tenant_id + order_id +-------------------+1.2.3.4.5.6.

代码实现:自动注入租户 ID

复制// 文件路径: src/main/java/com/icoderoad/tenant/TenantIdInterceptor.java @Intercepts({ @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}) }) public class TenantIdInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { Object parameter = invocation.getArgs()[1]; String tenantId = TenantContextHolder.getTenantId(); if (parameter instanceof BaseEntity) { ((BaseEntity) parameter).setTenantId(tenantId); } return invocation.proceed(); } }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.

资源配额控制

在多租户系统中,资源配额控制防止“资源独占”。

通用资源模型 复制// 文件路径: src/main/java/com/icoderoad/quota/TenantQuota.java @Entity @Table(name = "tenant_quota") public class TenantQuota { @Id private String tenantId; private Long storageQuota; private Long storageUsed; private Long apiCallQuota; private Long apiCallsUsed; private Integer concurrentUserQuota; public boolean canUseStorage(long size) { return (storageUsed + size) <= storageQuota; } }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17. 拦截器控制 API 调用 复制// 文件路径: src/main/java/com/icoderoad/quota/QuotaInterceptor.java public class QuotaInterceptor implements HandlerInterceptor { @Autowired private TenantQuotaService quotaService; @Override public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) throws Exception { String tenantId = getTenantIdFromRequest(req); TenantQuota quota = quotaService.getQuota(tenantId); if (quota.getApiCallsUsed() >= quota.getApiCallQuota()) { res.setStatus(HttpStatus.TOO_MANY_REQUESTS.value()); res.getWriter().write("API call quota exceeded"); return false; } quotaService.recordApiCall(tenantId); return true; } }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16. 分布式配额控制(Redis 方案) 复制// 文件路径: src/main/java/com/icoderoad/quota/RedisQuotaServiceImpl.java @Service public class RedisQuotaServiceImpl implements QuotaService { @Autowired private RedisTemplate<String, Long> redisTemplate; private static final String QUOTA_KEY_PREFIX = "tenant:quota:"; private static final String USAGE_KEY_PREFIX = "tenant:usage:"; @Override public boolean checkAndConsume(String tenantId, String resourceType, long amount) { String quotaKey = QUOTA_KEY_PREFIX + tenantId + ":" + resourceType; String usageKey = USAGE_KEY_PREFIX + tenantId + ":" + resourceType; String script = "local usage = redis.call(GET, KEYS[2]) or 0 " + "if usage + ARGV[1] > tonumber(ARGV[2]) then return 0 " + "else return redis.call(INCRBY, KEYS[2], ARGV[1]) end"; Long result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(quotaKey, usageKey), amount, redisTemplate.opsForValue().get(quotaKey)); return result != null && result > 0; } }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21. 认证与权限控制JWT 提取租户 ID 在过滤器中解析 JWT,并写入 TenantContextHolder。Spring Security 细粒度权限 通过 isTenantUser(tenantId) 方法实现基于租户的访问限制。最佳实践与方案选择数据库级隔离:适用于安全性要求极高、租户数量有限的场景。表级隔离:兼顾隔离性与成本。行级隔离:适用于大规模、多租户场景。

配额管理建议

多层控制:应用层 + 基础设施层双保险。提前预警:当资源使用接近阈值时提醒租户升级。弹性伸缩:结合计费与限流机制。服务器租用

结论

多租户 SaaS 架构的核心挑战在于:数据的干净隔离资源的公平分配

在数据层面,数据库/表/行级隔离各有优劣,需要根据业务规模与成本选择。在资源层面,通用配额模型 + Redis 分布式限流是高并发场景下的最佳实践。在安全层面,基于 JWT 的租户上下文和 Spring Security 的细粒度权限控制可确保租户之间权限清晰。

通过上述方案,我们已经在多个 SaaS 项目中实现了从数百到数十万租户的平滑扩展,既保证了数据安全,又实现了资源的高效利用。

未来的 SaaS 架构演进中,多租户隔离与配额管理仍会是不可或缺的基础能力。

分享到:

温馨提示:以上内容和图片整理于网络,仅供参考,希望对您有帮助!如有侵权行为请联系删除!

友情链接: