多租户架构设计思考
共享数据库,共享表
描述
所有租户的数据都在同一个数据库表内,以租户字段:tenant_id来区分。
优点
成本低,实现方式简单,适合中小型项目的快速实现。
缺点
- 数据隔离性差,某一个租户的数据量大的时候,会影响其他租户数据的操作效率。
- 需要在表上增加租户字段,对系统有一定的侵入性。
- 数据备份困难,因为所有租户的数据混合在一起,所以针对某个租户数据的备份、恢复会比较麻烦。
实现方式
**方式一:**编写Mybatis拦截器,拦截增删改查操作,动态的增加租户条件,如:
SELECT * FROM sys_user;
修改成:
SELECTG * FROM sys_user WHERE tenant_id = 100;
这种方案并不靠谱,因为动态修改SQL语句不是一个好的处理方式,如果SQL解析没有做好,或者出现复杂SQL,那么很容易产生bug。
**方式二:**编写Mybatis拦截器,拦截增删改查操作,判断是否有租户条件,如:
SELECT * FROM sys_user WHERE id=1;
使用jsqlparser工具解析SQL,判断出该SQL语句没有tenant_id的条件,那么抛出异常,不允许执行。
这种方案比较稳妥,因为只做判断不做修改。
查询操作的优先级不高,如果不在乎数据敏感,可以不拦截。
要注意的是修改操作,稍不注意容易被某一个租户影响其他租户的数据。
共享数据库,独立一张表
描述
所有租户的数据都在同一个数据库中,但是各自有一个独立的表,如:
# 1号租户的用户表
sys_user_1
# 2号租户的用户表
sys_user_2
...
优点
成本低,数据隔离性比共享表稍好,并且不用新增租户字段,对系统没有侵入性。
缺点
- 数据隔离性虽然比共享表好了些,但是因为仍在同一数据库下,所以某一个租户影响其他租户的数据操作效率问题依然存在。
- 数据备份困难的问题依然存在。
实现方式
**方式一:**编写Mybatis拦截器,拦截增删改查操作,动态的修改表名称,如:
SELECT * FROM sys_user;
修改成:
SELECT * FROM sys_user_1;
同样的,这种动态修改SQL语句的方式并不推荐,所以我们有另一种方式。
**方式二:**将表名作为参数传入
本来在Mapper.xml中,查询语句是这样的:
SELECT * FROM sys_user WHERE id = #{userId};
现在改成:
SELECT * FROM #{tableName} WHERE id = #{userId};
这样可以避免动态修改SQL语句操作。
独立数据库
描述
每个租户都单独分配一个数据库,数据完全独立,如:
database_1;
database_2;
...
优点
- 数据隔离性最好,不需要添加租户id字段,租户之间不会被彼此影响。
- 便于数据备份和恢复。
- 便于扩展。
缺点
- 经费成本高,尤其在有多个租户的情况下。
- 运维成本高。
结论
一般来说,当数据量不高的时候,选择共享数据库共享表的方式,表内加个租户id字段做区分,数据量或者用户量多起来,就可以直接升级到独立数据库的方式,因为独立表的方式处理起来是有些麻烦的,倒不如加个字段来的方便。
作者:失败的面
来源:juejin.cn/post/7282953307529953291
来源:juejin.cn/post/7282953307529953291