1. 为什么你的SaaS项目需要多租户数据隔离?
如果你正在开发一个SaaS(软件即服务)应用,比如一个在线客服系统、一个项目管理工具,或者一个企业内部的CRM,那你肯定绕不开一个核心问题:怎么把不同客户的数据彻底分开? 想象一下,A公司的销售数据绝对不能跑到B公司的后台里,这是最基本的安全和商业底线。
我刚入行那会儿,见过一些“简单粗暴”的做法。比如,所有租户的数据都塞进同一张表,然后加一个 tenant_id 字段来区分。这种方法开发起来是快,但隐患太大了。一次不小心的SQL查询忘了加 WHERE tenant_id = ?,就可能造成数据泄露。更别提随着数据量膨胀,这张表会变得无比臃肿,查询性能直线下降,维护起来简直是噩梦。
所以,现在成熟的SaaS架构,尤其是对数据安全要求高的场景,更倾向于 物理隔离。也就是每个租户独立一个数据库,或者至少是独立的数据库Schema。这样做的好处显而易见:数据绝对隔离、备份恢复灵活、性能影响小、扩展性强。但随之而来的技术挑战就是:我的应用代码怎么知道当前请求来自哪个租户?又该如何动态地连接到对应的数据库上去执行操作?
这就是我们今天要解决的实战问题。在Spring Boot生态里,JdbcTemplate 是很多开发者熟悉的、轻量级的数据访问工具。而 dynamic-datasource-spring-boot-starter 这个神器,能完美地帮我们管理多个数据源,并实现动态切换。两者结合,就能用一套代码,优雅地服务于成千上万个租户的独立数据库。下面,我就带你从零开始,一步步搭建这个体系。
2. 5分钟快速搭建你的多数据源环境
工欲善其事,必先利其器。我们先来把基础环境搭好。这里我假设你已经有一个Spring Boot 2.x的项目了,JDK用1.8或以上都行。
2.1 引入核心依赖
首先,打开你的 pom.xml 文件,添加 dynamic-datasource 的依赖。这里我强烈建议你使用较新的稳定版本,比如 3.5.0 或更高,因为它修复了很多早期版本的坑。
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.5.0</version>
</dependency>
当然,数据库驱动也不能少,比如我们用MySQL:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
注意:这个starter已经自带了数据源、事务等必要的Spring配置,所以你不需要再额外引入 spring-boot-starter-jdbc 或 spring-boot-starter-data-jpa(除非你明确要用JPA)。它本身就是基于这些构建的,避免了依赖冲突。
2.2 配置你的多个数据源
接下来是重头戏:application.yml 的配置。这里我们要定义多个租户对应的数据源。假设我们有两个初始租户:“公司A”和“公司B”,我们为它们分别创建了数据库 tenant_a_db 和 tenant_b_db。
spring:
datasource:
dynamic:
# 设置默认的数据源(主数据源),当没有指定租户或切换失败时使用
primary: master
# 是否启用严格模式。严格模式下,如果找不到指定的数据源会抛出异常。建议开发时开启,生产环境根据情况调整。
strict: true
datasource:
# 主数据源,可用于存储公共信息、租户元数据等
master:
url: jdbc:mysql://127.0.0.1:3306/common_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: your_password
driver-class-name: com.mysql.cj.jdbc.Driver
# 租户A的独立数据库
tenant_a_db:
url: jdbc:mysql://127.0.0.1:3306/tenant_a_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: tenant_a_user
password: password_a
driver-class-name: com.mysql.cj.jdbc.Driver
# 租户B的独立数据库
tenant_b_db:
url: jdbc:mysql://127.0.0.1:3306/tenant_b_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: tenant_b_user
password: password_b
driver-class-name: com.mysql.cj.jdbc.Driver
我来解释几个关键点:
primary:这个必须配置。它指定了当系统无法确定使用哪个数据源时的“保底”选项。通常我们会设置一个master或default库,用来存放所有租户的元数据信息(比如租户ID和数据库名的映射关系)。strict:我建议在开发阶段设为true。这样,如果你在代码里错误地引用了一个未配置的数据源(比如手滑写成了tenant_c_db),系统会立刻抛出异常,帮你快速定位问题。生产环境如果对稳定性要求极高,可以考虑设为false<


352

被折叠的 条评论
为什么被折叠?



