做网站 就上凡科建站,搜一搜百度,做短视频网站用哪家cms,做外贸都做哪些网站好免费下载SaaS多租户背景 很多平台类应用或系统#xff08;如电商CRM平台、仓库订单平台等等#xff09;#xff0c;它们的服务模型是围绕用户维度#xff08;这里的用户维度可以是一个卖家或品牌#xff0c;可以是一个仓库等#xff09;展开的。因此#xff0c;这类型的平台业务…SaaS多租户背景 很多平台类应用或系统如电商CRM平台、仓库订单平台等等它们的服务模型是围绕用户维度这里的用户维度可以是一个卖家或品牌可以是一个仓库等展开的。因此这类型的平台业务为了支持业务系统的水平扩展性业务的数据库通常是按用户维度进行水平切分。 可是当平台类应用的一些用户慢慢成长为大用户比如大品牌、大卖家、大仓库等后这些大用户由于其数据量或流量明显要比其它用户多得多容易导致以下的现象 ▶︎ 大用户所在分片会成为业务系统的热点占用大量的数据库资源其服务质量容易因资源受限导致不稳定
▶︎ 其它小用户容易受到大用户资源消耗的影响服务质量也受到影响。 最后就整个平台的业务系统的热点频现数据库访问不稳定业务服务受影响。 SaaS多租户模型作为一种应用的架构常用来解决业务的上述问题。在SaaS多租户模型中业务系统会需要服务多个用户每个用户或每批用户可以被视为一个租户。 这些不同的租户在业务系统内会使用共同的基础设施及其平台进行运行但来自不同租户的数据仍将被独立隔离因此通常租户拥有自己物理资源来单独存储与管理数据。所以SaaS多租户解决业务系统稳定性问题以及租户资源弹性定制的核心思路就是租户间的资源隔离及数据隔离。 在实际的不同应用场景下常见的 SaaS 多租户方案有两种 ▶︎ Schema级SaaS多租户 Schema 级 SaaS 多租户是指一个租户对应一个包含多个Table定义的Schema或一个Database在MySQL, Schema概念等同Database 不同租户的Schema会分布在不同的机器上如下图1所示实现资源隔离该方案适用于不同租户需要使用独立Schema运行的场景 ▶︎ Partition级SaaS多租户 Partition 级 SaaS 多租户是指一个租户会对应一个Table的一个或多个的分区或是一个Table的一部分rows)不同租户的Parittion会分布在不同的机器上如上图2所示以实现资源隔离该方案比较适用于不同租户需要使用统一Schema运行的场景。 从隔离程度来看 Schema 级 SaaS 多租户比 Partition 级 SaaS 多租户要隔离得更彻底但前者因为要维护众多的Schema会比后者会带来更高的运维成本及查询分析成本。 不过Partition级SaaS多租户通常要依赖中间件分库分表或分布式数据库分区功能不然单机数据库无法做到资源隔离才能运作而Schema级SaaS多租户则不需要用户自己搭建几个单机MySQL也可以运作起来准入门槛更低。 业务的问题
业务多租户场景
只说应用架构可能有些抽象为了方便读者更容易地理解 SaaS 多租户是如何帮助业务解决问题 本文将以一个真实的案例来进行阐述。 正马软件的班牛平台是国内领先的提供电商全周期客户服务的卖家订单管理平台以下简称B公司。它的业务系统需要维护多个不同品牌的众多卖家。通常一个品牌会有多个卖家比如一个品牌可能会开通多个线上店铺所以品牌与卖家是一对多的关系。 目前B公司的订单管理平台管理着超过50T的订单数据日QPS近3W不同品牌的订单量差异会比较大大品牌的订单可能是小品牌的订单量的近百倍或更高。一些大品牌除了订单量比其它品牌的大很多之外还会使用更高级的付费VIP服务比如要求订单数据独占资源与数据隔离、允许独立地统计分析自己品牌的订单数据等。 B公司为了解决不同品牌的数据的资源使用及其服务差异就会对它的卖家按品牌划分相当于一个品牌是一个租户 大品牌诉求 订单量大如订单数据存储的大小超过1T或2T数据存储量大 独占一组的存储资源、有独立访问分析数据的需求 该品牌的所有商家都必须同一组的存储资源 大品牌的大卖家150后边还会陆续增加 小品牌诉求 订单表小商家数目大6W卖家 共用一组存储资源 要求所有卖家数据在存储上均衡分布。 现在的核心问题是B公司的订单管理平台的数据库应该如何设计才能满足上述众多不同品牌及其大卖家对于不同资源使用与数据隔离的诉求 普通中间件方案及其问题 对于上述的业务场景B公司若不使用分布式数据库而是简单通过单机MySQL及一些开源的分库分表中间件自己搭建一套SaaS多租户方案比如将品牌及其卖家切分为租户进行租户的资源隔离。表面上这貌似可行但实际上业务会因此要面临更多更为棘手的问题。 首先是跨机分布式事务问题。绝大多数的分库分表中间件无法提供强一致分布式事务能力或者只能提供基于最终一致性的事务补偿方案这意味着业务需要做很多额外的应用改造成本才能尽量来避免跨机事务导致业务出现报错。 然后是 Schema 一致性问题。基于中间件分库分表无论是采用 Schema 级多租户及是 Partition 级多租户B公司的订单平台都要面临自己维护各个租户的 Schema 或 Table 的元数据一致性。比如MySQL的建删表、加减列、加减索引等常见的DDL操作中间件的方案无显然法保证平台所有租户的表能同时生效一旦执行中断必须靠人工介入来订正人力成本高。 接着是租户的数据迁移问题。基于SaaS多租户方案B公司若要给一个大品牌分配新的独立资源这自然免不了将租户数据从原来机器到新机器的数据迁移。这个数据迁移操作只能依赖额外的同步工具构建同步链路才能完成这中间切割过程甚至还需要业务停机。这意味业务执行添加一个新租户这一基本操作也会带来非常高昂的运维成本。 综合上述的分析B公司直接基于单机MySQL及一些中间件的SaaS多租户方案并不是一个成本低廉的方案。 SaaS多租户PolarDB分布式版方案 事实上在阿里云瑶池旗下的云原生数据库PolarDB分布式版 2.0(PolarDB for Xscale简称PolarDB-X) 中B公司已经可以通过结合非模板化二级分区与Locality两项能力来很好的解决其上述业务所面临的问题。为了方便读者更易理解以下先简单介绍下 PolarDB分布式版 2.0 的非模板化二级分区与Locality两项的功能。 非模板化二级分区
PolarDB分布式版 从 5.4.17 开始支持使用二级分区创建分区表。与其它分布式数据库所有不同PolarDB分布式版的二级分区除了语法能完全兼容原生MySQL二级分区语法外还额外扩展很多的二级分区的能力比如支持用户定义非模板化二级分区原生MySQL只支持模板化二级分区。 所谓的非模板化二级分区就是各个一级分区之下的二级分的分区数目及其边界值定义允许不一致如下所示 /* 一级分区 LIST COLUMNS 二级分区HASH分区 的非模板化组合分区 */
CREATE TABLE t_order /* 订单表 */ (id bigint not null auto_increment, sellerId bigint not null, buyerId bigint not null,primary key(id)
)
PARTITION BY LIST(sellerId/*卖家ID*/) /* */
SUBPARTITION BY HASH(sellerId)
(PARTITION pa VALUES IN (108109) SUBPARTITIONS 1 /* 一级分区 pa 之下有1个哈希分区, 保存大品牌 a 所有卖家数据 */,PARTITION pb VALUES IN (208209) SUBPARTITIONS 1 /* 一级分区 pb 之下有1个哈希分区, 保存大品牌 b 所有卖家数据 */,PARTITION pc VALUES IN (308309310)SUBPARTITIONS 2 /* 一级分区 pc 之下有2个哈希分区, 保存大品牌 c 所有卖家数据 */,PARTITION pDefault VALUES IN (DEFAULT)SUBPARTITIONS 64 /* 一级分区 pDefault 之下有64个哈希分区, 众多小品牌的卖家数据 */
);
基于上述的 LISTHASH 非模板化二级分区它能给应用直接带来的的效果是 对于大品牌的卖家相当一个租户可以将数据路由到单独的一组分区对于中小品牌可以将数据按哈希算法自动均衡到多个不同分区从而避免访问热点。 当大品牌与中小品牌的商家数据按LIST分区实现了分区级的隔离后那实现大品牌与中小品牌的存储资源的物理隔离也就自然而言的事了。在 PolarDB分布式版 2.0 中用户可以借助Locality的能力很容易地实现不同分区之间的资源隔离。 LOCALITY资源绑定 PolarDB分布式版支持通过LOCALITY关键字来指定数据库分区的实际存储资源位置PolarDB分布式版中存储资源由多个数据节点DN节点组成可以通过DN的ID进行位置分配以实现数据隔离或数据的均匀分布。它的具体语法如下所示 ALTER TABLE #tableName
MODIFY (SUB)PARTITION #(sub)partName
SET LOCALITYdndn1[, dn2,...]
例如B公司可以使用以下的SQL命令将 t_order 中的大品牌 pa 的数据全部单独挪到一个存储节点 dn4
ALTER TABLE t_order MODIFY PARTITION pa SET LOCALITYdndn4
在实际使用中用户可以通过 SHOW STORAGE 查询 PolarDB-X 的所有DN节点实例ID列表例如
mysql show storage;
---------------------------------------------------------------------------------------------------------------------------------
| STORAGE_INST_ID | LEADER_NODE | IS_HEALTHY | INST_KIND | DB_COUNT | GROUP_COUNT | STATUS | DELETABLE | DELAY | ACTIVE |
---------------------------------------------------------------------------------------------------------------------------------
| polardbx-storage-0-master | 10.0.x.1:3306 | true | MASTER | 41 | 66 | 0 | false | null | null |
| polardbx-storage-1-master | 10.0.x.1:3307 | true | MASTER | 41 | 53 | 0 | true | null | null |
| ...... | ...... | true | META_DB | 2 | 2 | 0 | false | null | null |
---------------------------------------------------------------------------------------------------------------------------------
设计SaaS多租户方案 回到之前B公司的例子B公司的核心需求是要实现大品牌与中小品牌的卖家数据及其存储资源的隔离。那么B公司可以在上述的二级分区的分区表的基础上通过再给每个一级分区增加对应的LOCALITY定义以指定一级分区及其所有二级分区所允许使用的存储资源那么业务就可以在建表阶段直接实现SaaS层多租户即品牌方存储资源的隔离如下所示 /* 一级分区list columns二级分区key 的非模板化组合分区 */
CREATE TABLE t_orders /* 订单表 */ (id bigint not null auto_increment, sellerId bigint not null, buyerId bigint not null,primary key(id)
)
PARTITION BY LIST(sellerId /* 卖家ID */ )
SUBPARTITION BY HASH(sellerId)
(PARTITION pa VALUES IN (108109,....) LOCALITYdndn16 /* 大品牌 pa 独占一个DN dn4 */SUBPARTITIONS 1,PARTITION pb VALUES IN (208209,....) LOCALITYdndn17 /* 大品牌 pb 独占一个DN dn5 */SUBPARTITIONS 1 ,PARTITION pc VALUES IN (308309310,...) LOCALITYdndn18,dn19 /* 大品牌 pc 独占两个DN: dn6 与 dn7 */SUBPARTITIONS 2,PARTITION pDefault VALUES IN (DEFAULT) /* 一级分区 pDefault 占用 dn0 ~ dn15 共16个DN, 中小品牌共享 */LOCALITYdndn0,dn1,...,dn2,dn15 SUBPARTITIONS 64
);
如上图所示通过Locality对各个一级分区的DN节点资源的绑定pa、pb、pc这3个大品牌的租户被分别配了DN16、DN17 与 DN18~DN19 3组的节点资源而中小卖家池的 pDefault 分区则被绑定了16个DN节点。 SaaS多租户运维管理 当二级分区及Locality能力解决了B公司对于不同品牌的多租户资源隔离后 那马上需要面临的问题自然是用户将如何有效便捷地管理这些多租户答案是 PolarDB分布式版 2.0 的分区管理能力。 PolarDB分布式版 2.0 对于分区表提供了一系列完备的灵活强大的分区管理命令如下图所示让用户能够仅仅通过简单SQL命令就可以实现在多租户场景下的不同运维变更的诉求。 接下来我们还是通过B公司的例子来单独介绍基于分区管理支持SaaS多租户场景下的常见的运维变更。 场景一基于修改LIST分区实现给租户添加新的卖家 以B公司为例B公司的一个租户对应的是一个品牌方一个品牌在B公司的平台通常会有多个卖家。因此当品牌方开了新的商铺时就需要将新的卖家ID加入到这个品牌方对应的租户资源之下。 借助 PolarDB-X 的 MODIFY PARTITION ADD/DROP VALUES 的功能可以方便地给 LIST 分区添加新的卖家ID如下所示 /* 给品牌 pb 增加新的卖家 205 */
ALTER TABLE t_orders MODIFY PARTITION pb ADD VALUES (205); 在这个DDL的执行中PolarDB分布式版会自动地从 LIST 的 DEFAULT 分区如果有显式定义 DEFAULT 分区的话抽取 sellerId205 的所有数据并迁移到 pb 分区中DDL 全过程 Online 业务应用几乎无感知。 场景二基于增加LIST分区实现给添加新租户并分配新的存储资源 诸如B公司这类订单管理平台平台上的各品牌的卖家通常会经历从无到有从小卖家发展成大卖家的过程。因此当一个品牌的小卖家发展成一个大卖家时该品牌就可能会让 B公司将它的卖家从中小品牌的卖家池比如DEFAULT分区中抽取了出来使之成为独立租户的VIP并为之分配单独的存储资源。 借助PolarDB分布式版的 ADD/DROP PARTITION 及其 Locality 的功能B公司可以很便捷地在线地完成上述场景的变更操作。例如B公司想将新的大品牌 pe 的大卖家 301 从 DEFAULT 分区中抽取出来并使之独占新的存储资源 new_dn 如下所示
/* 1.B公司在管控购买新的 CN/DN 的节点资源... */
/* 2.增加新的大卖家创建新分区并放置到特定的DN节点 */
ALTER TABLE t_orders ADD PARTITION (/* pDefault 分区里再抽取出新的大卖家 301 , 并命名为 pe, 并将其数据放置新节点 new_dn */PARTITION pe VALUES IN (301) LOCALITYdnnew_dn SUBPARTITIONS 1,
);
与MODIFY PARTITION类似这些ADD/DROP PARTITION的变更操作也属于Online DDL 这中间的数据迁移操作对业务应用近乎透明。 场景三基于分区级Locality支持租户内二级分区数据的重均衡 PolarDB分布式版的LIST KEY非模板化二级分区在多租户场景下能给用户提供一个重要的特性就是它允许不同的租户的二级哈希分区数目不一样。这样意味着不同的租户允许通过定义不同的二级分区数目可以使用不同数量的存储资源。 例如B公司的 t_orders 表的 LIST 分区定义中大品牌 pc 的一级 LIST 分区之下的二级分区数目是2并同时独占了2个DN节点来存储订单数据即 pc 分区的每个DN节点都分配一个二级分区。此外还有它的中小品牌的卖家所共享的 DEFAULT 分区之下有64个二级分区并且还独占 dn0 ~ dn15 共16个DN节点如下所示 PARTITION pDefault VALUES IN (DEFAULT) /* 一级分区 pDefault 占用 dn0 ~ dn3 共16个DN, 中小品牌共享 */LOCALITYdndn0,dn1,...,dn2,dn15 SUBPARTITIONS 64
可是DEFAULT分区里的众多中小卖家也可能存在一些热点比如20%的头部卖家可能占订单数量80%这些热点卖家如果分布不合理也可能会导致DEFAULT内部的16个DN节点间负载不均衡。 因此B公司需要面临的问题是该如何管理这64个二级分区的众多中小卖家的订单数据才能相对均衡地分布到这16个DN节点并保证系统整体的负载均衡呢这就是需要使用PolarDB分布式版的分区级Rebalance能力。 PolarDB分布式版的分区级 Rebalance 功能允许用户对一个一级分区内部的多个二级分区按一级分区的 Locality 进行自动的物理分片调度使这些二级分区在 Locality 所定义的DN节点上保持均衡分布。用户只需要执行一条SQL命令如下所示, 即可完成上述的均衡变更 REBLANCE TABLE t_orders PARTITIONSpDefault; 场景四基于分区选择及视图功能支持租户的数据查询及数据安全 PolarDB分布式版的分区表及Locality的SaaS级多租户能力对于诸如B公司这类订单管理平台除了能满足其对品方的数据隔离与资源隔离的诉求外还可以为业务提供更多的数据查询的能力。 比如B公司平台上的大品牌偶尔还需要使用诸如独立查询及分析自己的订单数据等的VIP服务。这些品牌方会通过B公司所提供一些Web SQL工具来直接查询分析自己的订单数据比如查询重要客户的订单数目等。 可是B公司作为平台性的系统它需要保证不同租户间的数据安全及其隔离即租户查询订单数据只能看到自己的数据无法看到其它租户的任何数据。 那么基于PolarDB分布式版的分区表B公司是如何解决不同租户的数据隔离的问题呢答案是借助分区选择与视图定义。 比如B公司如果想授权它的租户 pb 单独查询及分析它自己的订单数据它的Web SQL工具将会自动化地使用类似以下的SQL命令提前在PolarDB分布式版上为该租户 pb 创建出对应的只读视图 t_order_pb_view CREATE VIEW t_order_pb_view AS
SELECT *
FROM t_orders PARTITIONpb) /* t_orders 表的数据只会返回 pb分区以及下所有二级分区 */ ; 然后平台再通过对租户 pb 账号信息进行自动化的相关授权操作后租户 pb 在其所提供的 Web SQL工具里登录后将只允许看到 t_order_pb_view 这个只读视图。 那么假如租户要执行诸如下边所示的这类的统计订单总数的视图查询 /* 大租户 pb 查询订单数据的SQL统计订单数目 */
SELECT COUNT(1) FROM t_order_pb_view; PolarDB分布式版将自动地把视图 t_order_pb_view 替换为对应的子查询 /* 大租户 pb 查询订单数据的SQL统计订单数目 */
SELECT COUNT(1) FROM
(SELECT * FROM t_orders PARTITIONpb)
) as t_order_pb_view; 如此一来基于分区选择语法的限定视图 t_order_pb_view 将只允许返回 pb 分区的数据。这样租户 pb 无法查询到其它租户的卖家订单数据从而达到数据隔离的效果。 实践总结 PolarDB分布式版分区表及其配套的灵活的管理语法在不同的业务场景下可以包装出各种业务模型。比如本文所介绍的基于非模板化二级分区 Locality能力的所构建的SaaS多租户就是其中的经典用法之一。 事实上本文所提及的真实案例的B公司的商家订单管理系统已经基于上述的 PolarDB分布式版 2.0 的 SaaS 多租户方案成功上线其应用架构如下图所示目前它所提供的平台负责管理着超过50T的订单数据。 但是B公司的案例显然是一个能够复制并推而广之的案例。比如它的租户维度--品牌可以很容易联想到其它的业务维度并可以构建类似的实践比如各大仓库物流单管理、直播平台各直播室的观众送礼物的数据管理、各大城市交通监控数据管理、各大省份气象监控数据收集等等。 简单总结一下最佳实践若业务场景存在 ▶︎ 需要对数据按某个维度如地域、仓库、商家或品牌等进行水平切分划分多个业务单元
▶︎ 还需要为切分后的业务单元进行不同的资源配置及数据的物理隔离 涉及到以上几点的用户都可以参考使用本文SaaS多租户方案进行数据库设计。