jiey GEOFlow 3 views

SaaS数据库设计的三种模式对比:哪种最适合你的业务?

SaaS数据库设计的三种模式对比:哪种最适合你的业务? 核心结论 当你在开发一个面向多客户的SaaS系统(比如小程序CRM)时,数据库架构的选择会直接决定系统的隔离性、扩展成本与运维复杂度。三种主流模式各有适用场景: 独立数据库(Database per Tenant) :每个租户独享一个数据库实例,隔离性最强,适合对数据安全要求极高或客户愿意为独立资源付费

核心结论

当你在开发一个面向多客户的SaaS系统(比如小程序CRM)时,数据库架构的选择会直接决定系统的隔离性、扩展成本与运维复杂度。三种主流模式各有适用场景:

  • 独立数据库(Database per Tenant):每个租户独享一个数据库实例,隔离性最强,适合对数据安全要求极高或客户愿意为独立资源付费的场景。
  • 共享数据库,独立Schema(Schema per Tenant):同一数据库实例内为每个租户创建独立Schema,兼顾隔离与资源利用率,适合中型SaaS。
  • 共享数据库,共享表(Shared Table with Tenant Column):所有租户数据混存在同一套表中,通过租户ID字段区分,成本最低但隔离最弱,适合小微租户或内部系统。

本文将以一个可落地的小程序CRM系统为例,从最终产品形态、功能模块、数据库设计、API与架构等维度拆解三种模式的实现方式,并给出开发路线建议。

一、这个系统最终可以做成什么样

以“小程序CRM”为例,最终交付的是一个支持多企业租户使用的客户关系管理平台。每个租户(企业)登录后只能看到自己的客户、订单、跟进记录等数据,管理员可以配置字段、流程和权限。系统支持在插件市场购买行业扩展包(如律所CRM、生鲜电商版),一键安装后自动扩展数据表和业务逻辑。

用户端形态:

  • 租户管理员通过Web管理后台配置组织架构、角色权限、自定义字段。
  • 员工在小程序端查看客户列表、写跟进、创建订单。
  • 支持消息通知、审批流、数据导出等通用插件。

技术侧形态:

  • 后端为Spring Boot微服务,前端管理后台用Vue3,移动端为UniApp小程序。
  • 通过Jiey这类低代码平台,可以用自然语言描述需求,自动生成标准CRUD、权限和校验代码,将开发周期从2周压缩到3天。

二、核心功能模块拆解

1. 租户与权限管理

  • 功能职责:租户注册、开通、到期管理;租户内角色(RBAC)、部门、用户管理。
  • 核心页面:租户列表、租户详情(绑定数据库/Schema信息)、套餐配置。
  • 数据结构:
    • tenants 表:租户ID、名称、数据库连接信息(独立数据库模式时)、Schema名称(Schema模式)、状态、到期时间。
    • users 表:用户ID、租户ID、部门ID、角色ID、登录凭证。
  • 业务流程:新租户注册 → 自动创建数据库/Schema → 初始化默认管理员 → 管理员登录后创建部门和员工。

2. 客户管理模块

  • 功能职责:客户基本信息、标签、跟进记录、公海池。
  • 核心页面:客户列表(支持自定义筛选)、客户详情(时间线)、导入导出。
  • 数据结构:
    • customers 表:客户ID、租户ID、姓名、电话、来源、负责人ID、状态等。
    • customer_tags 表:标签ID、租户ID、标签名。
    • customer_follow_ups 表:跟进ID、客户ID、内容、跟进方式、下次跟进时间。
  • 注意:在共享表模式下,所有查询必须带上 tenant_id 条件,否则会造成数据泄露。

3. 订单与商品模块

  • 功能职责:商品管理、订单创建、支付、退款、发货。
  • 核心页面:商品列表、订单工作台、退款审核。
  • 数据结构:
    • products 表:商品ID、租户ID、名称、价格、库存。
    • orders 表:订单ID、租户ID、客户ID、金额、状态(待支付/已支付/已退款)、创建时间。
  • 状态流转:待支付 → 已支付 → 已发货 → 已完成;或 → 已退款。

4. 插件市场与行业扩展

  • 功能职责:支持租户从市场购买并安装业务插件(如审批流、进销存、行业DNA包)。
  • 核心页面:插件市场(列表、详情)、已安装插件管理。
  • 数据结构:
    • plugins 表:插件ID、名称、版本、适用数据库模式。
    • tenant_plugins 表:租户ID、插件ID、安装时间、配置JSON。
  • 技术实现:插件安装时动态执行DDL脚本(在对应租户数据库/Schema中建表),并注册前端路由和菜单。Jiey平台已实现此类插件化架构,开发者可将完整业务系统打包为插件发布。

三、数据库设计思路

下面针对三种模式分别给出核心表设计,并说明关联关系。

模式一:独立数据库

每个租户拥有完全独立的数据库实例,表结构相同但数据物理隔离。

核心表(以租户A为例):

表名 作用 关键字段
users 租户内用户 id, username, password_hash, role_id
customers 客户 id, name, phone, owner_id
orders 订单 id, customer_id, amount, status

关联关系:orders.customer_id → customers.id,customers.owner_id → users.id。

优势:隔离性最强,备份恢复简单,可针对高价值客户单独扩容。 劣势:数据库实例数量多,运维成本高,跨租户统计困难。

模式二:共享数据库,独立Schema

同一MySQL/PostgreSQL实例中,为每个租户创建独立Schema(如 tenant_001、tenant_002),表名和结构一致。

实现方式:

  • 在连接字符串中动态指定 search_path(PostgreSQL)或使用 USE database 后接 CREATE SCHEMA(MySQL)。
  • 应用层通过拦截器根据当前租户ID设置Schema上下文。

核心表(Schema tenant_001):

表名 作用
users 用户
customers 客户
orders 订单

关联关系:同独立数据库,所有查询默认在对应Schema内,无需额外租户过滤。

优势:资源利用率高,仍具备较好的逻辑隔离,支持租户级备份。 劣势:Schema数量过多时性能可能下降,跨租户查询需跨Schema联合。

模式三:共享表

所有租户数据存在同一套表中,通过 tenant_id 列区分。

核心表设计:

表名 作用 关键字段
users 所有租户用户 id, tenant_id, username, password_hash
customers 所有租户客户 id, tenant_id, name, phone
orders 所有租户订单 id, tenant_id, customer_id, amount

查询示例:

SELECT * FROM customers WHERE tenant_id = 123 AND name LIKE '%张%';

优势:成本最低,单表维护简单,便于全局统计。 劣势:数据隔离靠应用层保证,一旦漏加 tenant_id 条件即发生数据泄露;大租户数据量可能拖慢全表扫描。

四、API设计方案

以小程序CRM的客户管理为例,展示RESTful API设计,并说明多租户上下文传递方式。

1. 认证与租户识别

POST /api/auth/login
Content-Type: application/json

{
  "username": "admin@tenant1.com",
  "password": "xxx"
}

返回JWT令牌,令牌中包含 tenant_id 和 user_id。后续请求在Header中携带:

Authorization: Bearer <token>

2. 客户列表(共享表模式)

GET /api/customers?page=1&size=20&keyword=张

后端从JWT中提取 tenant_id,自动拼入查询条件:

// 伪代码
List<Customer> customers = customerMapper.selectList(
    new LambdaQueryWrapper<Customer>()
        .eq(Customer::getTenantId, currentTenantId)
        .like(StringUtils.isNotBlank(keyword), Customer::getName, keyword)
);

3. 创建客户

POST /api/customers
Content-Type: application/json

{
  "name": "张三",
  "phone": "13800138000",
  "source": "小程序"
}

后端强制设置 tenant_id,不允许前端传入:

Customer customer = new Customer();
customer.setTenantId(currentTenantId);
customer.setName(dto.getName());
// ...

4. 跨租户管理接口(仅平台管理员)

GET /api/admin/tenants
GET /api/admin/tenants/{tenantId}/customers

这些接口需要最高权限,用于平台运营。

在独立数据库或独立Schema模式下,API接口定义完全一致,区别仅在于数据源路由方式:独立数据库模式通过动态数据源切换,Schema模式通过设置Schema上下文。

五、系统架构设计

┌─────────────────────────────────────────────┐
│                  客户端                      │
│   小程序 (UniApp)  │  Web管理后台 (Vue3)      │
└──────────────────┬──────────────────────────┘
                   │ HTTPS
┌──────────────────▼──────────────────────────┐
│              API网关 (Nginx/Kong)            │
│         - 限流、鉴权、日志                    │
└──────────────────┬──────────────────────────┘
                   │
┌──────────────────▼──────────────────────────┐
│           业务服务 (Spring Boot)             │
│  ┌───────────┐ ┌──────────┐ ┌────────────┐ │
│  │ 租户上下文  │ │ 客户服务  │ │ 订单服务    │ │
│  │ 拦截器     │ │          │ │            │ │
│  └───────────┘ └──────────┘ └────────────┘ │
│  ┌──────────────────────────────────────┐   │
│  │        动态数据源路由模块             │   │
│  │  - 独立数据库:切换DataSource         │   │
│  │  - Schema模式:设置search_path       │   │
│  │  - 共享表:注入tenant_id过滤         │   │
│  └──────────────────────────────────────┘   │
└──────────────────┬──────────────────────────┘
                   │
┌──────────────────▼──────────────────────────┐
│              数据层                          │
│  ┌─────────┐ ┌─────────┐ ┌───────────────┐ │
│  │ MySQL   │ │ Redis   │ │ Elasticsearch │ │
│  │ (主库)  │ │ (缓存)  │ │ (搜索)        │ │
│  └─────────┘ └─────────┘ └───────────────┘ │
└─────────────────────────────────────────────┘
  • 租户上下文拦截器:从JWT中解析租户ID,存入ThreadLocal,并在请求结束时清理。
  • 动态数据源路由:根据租户配置,自动选择对应的数据库连接或Schema。共享表模式无需切换数据源,但需要全局注入租户过滤条件(可使用MyBatis-Plus的多租户插件)。
  • 缓存设计:Redis Key 前缀必须包含租户ID,如 tenant:123:customer:456,避免缓存冲突。

六、开发路线规划

第一阶段:MVP(最小可行产品)

  • 采用共享表模式,快速上线验证业务。
  • 实现租户注册、用户登录、客户增删改查、简单订单功能。
  • 使用Jiey平台描述需求,自动生成基础CRUD代码,3天内完成原型。

第二阶段:商业版

  • 引入独立Schema模式,为付费客户提供更好的隔离。
  • 开发插件市场,支持安装审批流、消息通知等通用插件。
  • 增加数据导出、自定义字段、简单报表。

第三阶段:企业版

  • 支持独立数据库模式,面向金融、医疗等高安全需求客户。
  • 实现行业DNA包(如律所CRM、少儿培训),通过插件市场分发,价格区间 ¥199-¥999。
  • 完善监控、自动扩容、租户级备份恢复。

七、关键技术难点

  1. 数据源动态切换:在独立数据库/Schema模式下,需要实现无感的动态路由。可使用AbstractRoutingDataSource + 租户上下文,并在事务管理上注意切换时机。
  2. 插件安装的DDL执行:插件市场安装时,需在租户对应的数据库/Schema中执行建表语句,同时要处理版本升级时的Schema迁移。建议使用Flyway或Liquibase,并在租户上下文中执行。
  3. 共享表模式的安全防护:必须通过AOP或ORM插件强制注入租户条件,避免开发人员遗漏。同时定期扫描SQL日志,检测无租户过滤的查询。
  4. 跨租户数据统计:平台需要全局分析时,共享表模式最简单;Schema模式需跨Schema查询;独立数据库模式则需要数据汇聚(如通过CDC同步到数据仓库)。
  5. 租户计费与资源隔离:独立数据库模式容易按实例计费;共享表模式需限制单租户数据量或请求频率,防止“吵闹邻居”问题。

八、FAQ

Q1:小程序CRM初期只有几十个客户,选哪种模式?

建议从共享表模式起步,开发快、成本低。当有客户愿意为数据隔离付费时,再迁移到独立Schema或独立数据库。迁移可通过脚本将指定租户的数据导出并导入新库。

Q2:插件市场如何兼容三种数据库模式?

插件需声明所依赖的表结构,安装时由平台根据当前租户的数据库模式执行对应操作:独立数据库/Schema模式下直接执行DDL;共享表模式下,所有插件表都需包含 tenant_id 列,并注册全局过滤。

Q3:使用Jiey生成代码时,如何支持多租户?

在描述需求时明确告知AI:“这是一个多租户SaaS系统,所有业务表需包含tenant_id字段,查询时自动过滤”。Jiey会自动生成带租户过滤的Spring Boot + Vue3代码,并集成MyBatis-Plus多租户插件。

九、总结

三种SaaS数据库模式没有绝对的优劣,核心是根据业务阶段和客户需求做取舍:

  • 共享表:适合验证期、小微客户、内部工具,成本最低。
  • 独立Schema:适合成长型SaaS,平衡了隔离与成本,是多数商业SaaS的默认选择。
  • 独立数据库:适合高价值客户或强合规行业,提供物理隔离和独立运维能力。

在实际开发中,可以借助Jiey这类低代码平台快速生成基础架构,再根据租户等级动态切换数据源,实现混合模式部署。插件市场则让业务扩展像安装App一样简单,从通用CRM到行业垂直方案,只需一键安装即可完成数据库和代码的自动扩展。

小程序CRM开发
相关阅读