手撸一个”低配版“MongoDB数据库:一、MongoDB多租户中间件简介
前言
最近一年的工作中,为了解决在 SaaS 类多租户场景下,MongoDB 数据库集群的水平伸缩、数据隔离、资源利用等多方面的问题,我独立设计并研发了一个 MongoDB 数据库的代理中间件。
在此将该中间件解决的问题、使用场景及背后的实现技术,做一个记录和总结。
业务需求
现状
公司的核心产品是基于企业微信生态的 SCRM 产品,这是一个典型的 toB 类型的 SaaS 服务。基于创始团队的技术背景及SCRM类业务的特点,后端服务的业务数据库采用的是著名的文档型数据库 MongoDB。
众所周知,SaaS类应用是典型的多租户(Multi-Tenancy)系统,其系统架构的设计在数据层通常可以采用4种不同的资源隔离策略:
- 独立DB实例
- 共享DB实例,独立Schema
- 共享DB实例,共享Schema,独立Table
- 共享DB实例,共享Schema,共享Table
基于以上的第3种隔离策略,公司初期的系统,设计了一套下图所示的数据访问层架构:

在该架构下:
- 共享DB实例 :整个平台的业务数据库采用了一套 MongoDB 的分片集群(Shard Cluster),通过向集群中增加 Shard 副本集,来实现数据层的水平伸缩;
- 共享Schema :利用 MongoDB 分片集群的“路由”功能,将每个Shard副本集的资源,逻辑上分为 M 个 Schema(即MongoDB中的 Database),每个Schema用于承载 N 个平台租户的数据。
- 独立Table :为同一个Schema中的每个租户创建一套完整的业务数据库表(Collection),通过在数据表名称上添加租户ID前缀,对租户进行区分;
- 租户数据映射与路由 :为业务层各服务使用的 Java、Golang 等语言开发一套数据访问层SDK,包含了各租户与Schema和数据表名等的映射策略、数据访问路由策略、数据访问层CRUD方法,所有业务服务统一使用 SDK 访问数据库。
问题
随着业务越来约复杂、平台入驻的企业越来越多,该数据层架构的缺陷也越来越突出,并经常导致各种问题,例如:
- 数据库升级困难:如果需要对 MongoDB 版本进行升级,需要先将整个分片集群停机,也就意味着整个平台的业务需要长时间的中断,这对入驻平台的用户来说是无法接受的。也就导致了生产环境的 MongoDB 一直无法进行版本升级或 Bug 修复。
- 数据库不支持事务操作:这其实是上一个问题的延续,团队最初使用的 MongoDB主版本号为
4.0,该版本在分片集群模式下,不支持事务操作。因为无法进行版本升级,也就导致开发人员在业务层需要对各种需要保证事务的场景做许多额外的开发工作来保证数据的一致性。 - 资源浪费:每个租户根据开通的业务功能,在初始化账号时都会为其创建 300~1000 张数据表及上千个索引。在 MongoDB 中这些数据表及索引都会驻留在内存中,占用大量内存资源及文件分配符。但往往80%以上的数据表中都是只有几条数据。而80%以上的租户,因为体量不大,业务操作量其实是非常少的。这也就造成了数据库资源的极大浪费。随着入驻企业越来越多,整个集群的服务器规模达到将近上百台,日常的运维和费用支出都成了一个大问题。
- 资源平均分配,无法支持大体量用户业务。该架构下最多也只能让一个租户独占一个副本集的资源,对于一些大体量用户,因为数据量和访问量巨大,一个副本集根本无法支持,也就导致业务层面用户体验和性能表现无法满足大型KA用户的需求。并且由于各种业务层和架构和代码设计的不合理,经常出现一个KA用户的慢操作,拖垮整个平台其他用户的情况。
- 租户间相互影响。因为多个租户共享一个Schema。因为没有对租户进行体量、业务量、行业、业务特性等的分类管理,也就经常出现Schema内多个租户间争抢资源的问题。
- SDK不支持分表等技术手段。对于一些数据量达到千万、甚至上亿行记录的热点表,因为SDK不支持分库分表Sharding,业务层也没有相应的分表机制,导致这些热点表,读写性能随数据量增长急剧下降。当然如果做了分库分表,在现有的多租户机制下,又会造成表和索引的数量急剧增加的副作用。
解决方案
作为技术中台,为了解决以上这些问题,为业务研发团队提供更好的数据层服务。我重新设计了一套数据库层的多租户架构,并配套开发了一个 MongoDB 的代理中间件。
该解决方案如下:
逻辑架构

系统架构

该解决方案:
- 上层业务服务不再直连具体的MongoDB服务器
- 上层业务服务可以继续沿用原有的数据访问层架构,也可以不再关心多租户的路由策略将这部分需求交给中间件来处理,做大最大化的兼容现有业务服务。
- 中间件后端依据业务需求可以部署多个MongoDB单实例,也可以部署副本集和分片集群
- 不同行业或体量的租户,可以比较精确的分配需要的数据库资源;
