目录
前言
数据行
数据表
基于业务场景
基于数据量
数据库
数据源表
动态数据源
前言
多租户系统是一种将多个客户的数据和应用程序分开的系统,每个客户被视为一个独立的租户,互不干扰。实现多租户系统的关键之一是确保数据的隔离。
数据隔离的方案可以分成三种:
- 数据行级别:所有租户的数据都在同一张表内,表中设计一个租户ID字段来区分不同租户,只需增加租户ID处理逻辑。这种方案最简单,也适用于绝大多数数据量不大,并发量也不高的小系统
- 数据表级别:每个租户在同一个数据库中,使用不同的数据表来存储数据,多租户操作不需要切换数据源,相对没那么复杂,但是需要为数据表增加租户标识以及在应用程序中增加表名处理逻辑,适用于单个租户数据量大,但是并发不高的系统
- 数据库级别:每个租户使用独立的数据库来存储数据,隔离性最高,但在应用程序中就需要管理多个数据源,增加了管理的复杂性。只有单个租户数据量大且并发高才需要考虑这种隔离方式
之前公司的产品是一个多租户的SAAS平台,是一个面向企业办公的平台,每个企业其实就相当于一个租户。实际使用中采用可 数据行+数据表 混合的数据隔离方案,大多数业务场景中是数据行级别,即所有的数据都在同一张表下,用租户ID来区分。少部分业务场景则是数据表级别,每个租户都有自己的数据表,表名使用租户ID做后缀区分。
数据行
基于绝大多数没有什么并发量和数据量的前提,使用数据行隔离级别就足够了,方案简单易用。
以之前公司的产品作为例子,使用的就是数据行隔离级别。
系统中的租户其实就是企业,在每张数据表中都设计一个企业ID(company_id),不同企业的数据通过企业ID来区分。
在业务操作时都要带上企业ID处理,比如查询时带上企业ID过滤条件,使得查询只查出该企业下的数据;保存数据时带上企业ID,将企业ID保存到表中,标识该行数据是这个企业的,以便查询时过滤。
数据表
采用数据表级别的隔离方案,一般来说,有两个原因。
- 业务场景
- 数据量大
同样以之前公司的产品作为例子,少部分业务场景就是用了数据表级别的隔离方案。
基于业务场景
公司的产品是面向企业的,每个企业都能基于平台去设计自己的企业应用,实现自己自定义的业务逻辑。简单来说,平台上能注册多个企业,企业间数据互相隔离,每个企业又能够设计多个应用,每个应用的数据也是相互隔离的。
这种业务场景下,针对企业下每个应用的数据隔离,如果还采用数据行隔离的方案,那么只使用企业ID一个字段是区分不了每个应用的数据的,还需要加上应用ID才能够区分;而且由于存在多层的嵌套关系(一个平台注册多个企业,一个企业又可以设计多个应用),显得一张表下储存的数据种类很臃肿。
以上这种场景,可以使用数据表级别隔离方案来优化,每个企业下的应用使用不同的表来存储,表使用企业ID后缀来做区分,表中的数据再通过数据行隔离的方式,通过应用ID(app_id)来区分同一个企业下的不同应用。
这样做的好处就是数据之间的隔离关系显得很清晰,通过表名就能直接了解到数据之间隔离关系。但是个人感觉这种方式是不必要的,从功能实现上来说,直接用数据行隔离和用数据表隔离其实都能够实现,甚至数据行隔离实现还比较简单,使用数据表隔离还需要自己处理表名的逻辑。
基于数据量
基于某些业务场景采用数据表隔离是不必要的,但如果是单个租户数据量大的情况下,那么使用数据表隔离就是必要的了。
公司的产品是面向办公的,每个企业都可以设计自己的表单,表单是办公产品的核心功能,它的数据量是相对较大的。而数据量大的表就会存在查询性能下降等问题,针对数据量大的问题,其实就是使用分表的思路来解决。
在多租户系统的设计中,预先估计了某些表的数据量会比较大,便可以直接提前分表,也就是直接使用数据表隔离级别。在上面的场景中,根据企业ID来进行分表,将数据分散到多个表下,解决单表数据量过大的问题。
数据库
在MySQL中,其实database和schema是没有区别的,但是在PostgreSQL里,database和schema则是有区别的,一个database下有多个schema。
这里的数据库隔离级别,包含了不同的database,也包含了同一个database,不同的schema这种情况。因为在实际使用中, 都需要动态切换数据源,所以将以上两种情况放在一起讨论。
虽然在实际接触的系统中没有使用过这种隔离级别,但是学习最重要的就是做到举一反三,所以这里也写一些数据库隔离级别的实现思路。
数据源表
一般系统都是固定的数据库,所以数据库连接信息都是直接放在项目配置文件中。而多租户系统使用数据库隔离方式,则需要实现数据源切换,就得设计一张表来存储数据库的连接信息
相关表字段如下
- id 主键id
- tenent_id 租户id
- url 数据库url
- username 用户名
- password 密码
- driver 数据库驱动
- schema 视图
动态数据源
有了数据源表,那么在应用程序中就需要实现动态数据源了,在运行中根据不同的租户ID来获取对应的数据源元信息,构造数据源,放入本地缓存中,并且将这个数据源加入到动态数据中,以便在运行时切换数据源。
具体到Spring框架中,就是使用 切面/拦截器 + AbstractRoutingDataSource + ThreadLocal
使用切面(AOP)或者 拦截器(Interceptor)拦截请求,提取当前请求的租户ID,如果缓存中不存在该租户数据源,则查询数据源表,构造数据源,并加入到动态数据源中
动态数据源再根据租户ID决定运行时的数据源
动态数据源相关的使用可以参考以下:
Spring Boot 动态数据源-CSDN博客文章浏览阅读616次,点赞11次,收藏24次。大多数系统中,都需要数据库来持久化数据,在大多数情况下,一个系统只需要配置一个数据源便能够完成所有业务的查询,保存操作。也存在一个系统需要多个数据源的情况,不同的数据源对应不同的业务操作,这种场景下配置多个数据源,并且在代码中维护多套dao层就可以了。还存在一种业务场景,所有的业务操作都是一样的,只有操作的数据源的不同,如果用多套dao层来实现,由于业务操作都一样,会出现多块一模一样的代码,这样的冗余代码是我们不希望看到,不利于维护。这种业务场景就很适合用动态数据源来实现。https://blog.csdn.net/typeracer/article/details/140814168