构建可扩展模型的几种方案

6,491 阅读8分钟

我们来自字节跳动飞书商业应用研发部(Lark Business Applications),目前我们在北京、深圳、上海、武汉、杭州、成都、广州、三亚都设立了办公区域。我们关注的产品领域主要在企业经验管理软件上,包括飞书 OKR、飞书绩效、飞书招聘、飞书人事等 HCM 领域系统,也包括飞书审批、OA、法务、财务、采购、差旅与报销等系统。欢迎各位加入我们。

本文作者:飞书商业应用研发部 许家强

欢迎大家关注飞书技术,每周定期更新飞书技术团队技术干货内容,想看什么内容,欢迎大家评论区留言~

背景

在 SaaS 平台建设过程中存在一个问题:由于 B 类业务的复杂多变,不同企业的业务流程和管理机制存在很大差异,而市面上的标准化 SaaS 产品,较难满足企业的差异化、定制化需求。

因此需要有一套基于高度可扩展的技术框架,用于支撑 B 类业务中灵活变化、可定制化性强的场景。APaaS 就是用于解决平台扩展性的杀手锏,APaaS 平台不仅提供了 SaaS 产品标准化的产品能力,同时,允许使用方通过零代码、轻量的配置化方式,或是以较低成本在 APaaS 平台进行二次开发,丰富和扩展产品的功能。 本文介绍当前业界用于构建可扩展性模型采用的几种方法。

技术选型

APaaS 平台需要有高度可扩展的模型设计。然而,传统的企业应用的建模方法,通常是基于关系型数据库、面向对象进行建模,该方式预先定义好了模型的结构,如果需要扩展必须通过修改模型( DDL 变更),代价较大;更为致命的是,一个平台上所有租户共享一份数据结构和模型,这套方案显然不适用于 APaaS 平台。

基于元数据驱动的模型

关键概念

  • 对象:模型的实例。定义了一个特定的信息论域。如一个具体的订单。
  • 模型:一个模型是一个元模型的实例。模型层的主要责任是定义描述信息论域的语言。在建模层上的对象的例子如:订单模型。
  • 元模型:一个元模型是一个元元模型的实例。元模型层的主要责任是定义描述模型的语言。一般来说,元模型比定义它的元元模型更加精细,尤其是当它们定义动态语义时。
  • 元元模型:构成了元建模( metamodeling )体系结构的基础结构。这一层的主要责任是定义描述元模型的语言。一个元元模型定义了这样一个模型,它比元模型具有更高的抽象级别,而且比它定义的元模型更加简洁。一个元元模型能够定义多个元模型,而每个元模型也可以与多个元元模型相关联。通常所说的相关联的元模型和元元模型共享同一个设计原理和构造,也不是绝对的准则。每一层都需要维持自己设计的完整性。在元元模型层上的元元对象的例子有:元类、元属性元操作。 salesforce 的 APaaS 平台,就是基于元模型设计的。其主要的模型如下:

image.png

多租户隔离

  • OrgId:表示租户id,用于多租户隔离。
  • Guid:区分该记录的唯一标识。

模型描述

  • Object:描述模型的基本信息。如PO、商品、需求单、付款单都是一个Object。
  • Field:描述模型的元结构信息,其中FieldNum维护了该字段在Object的顺序编码。
  • Data:存储模型对象的数据,定义Value0、Value1...、Value500 共500个固定字段,用于存储在Field定义的字段,value的下标,正好与Field中的FieldNum对应。
  • Indexs: 定义了模型的索引字段的信息,用于快速检索。salesforce在写入数据时,将同时写Data和Index表。
  • UniqueIndexs:定义了模型的唯一索引字段信息,利用数据库自身的唯一约束能力保证模型的完整性。salesforce在写入数据时,将同时写Data、UniqueIndexs表。

运行机制

基于上述元模型驱动的架构,达到了几个目标:

  • 多租户,每个租户的数据完全隔离
  • 模型结构是高度可定制化的,其结构不是在系统部署前预先定义好的,是在程序运行时基于用户配置的元模型,动态加载、拼装而成的,在内存中看到的是组装后的真正对象视图

查询的实现:

  1. 前端在查询数据时,平台需要从Object、Field获取元模型的结构定义,确定了对象类型和对象的结构
  2. 根据对象的索引,到Index、UniqueIndexs表查询数据的主键,即定位Data的Guid
  3. 查询Data获取数据
  4. 根据对象的结构Field,将扁平化的Data,对应value0、value1...value500,组装成对象视图

写入的实现:

  1. 前端在写入数据时,平台需要从Object、Field获取元模型的结构定义,确定了对象类型和对象的结构
  2. 根据对象的结构Field,将对象视图的数据,组装到扁平化的Data中,对应value0、value1...value500
  3. 根据对象的索引设置,按需写入Index、UniqueIndexs表,用于维护索引数据。

方案缺点

  • 实现的成本较高,框架逻辑复杂
  • 分库分表比较难做,会遇到性能瓶颈

基于key-value的纵表设计

方案描述

所有的数据都基于纵表进行存储。

  • Object:按租户id进行隔离。一个Object表示一种业务实体类型,如PO、商品、采购申请单。
  • Filed:维护每个对象的结构和数据。每个租户的结构是不同的。例如,一个商品有名称、属性、价格、库存,分成多行记录存储在Filed。

方案缺点

  • 数据很容易膨胀,对于B类业务,一个业务实体可能有几十、上百种属性,水平扩展能力差
  • 一次写事务要操作上百条数据,大事务的处理会影响系统的吞吐量
  • 查询时要涉及表关联查询,且会查大量数据,频繁IO导致系统性能不佳。
  • 检索查询支撑能力较差

基于NOSQL的存储设计

方案描述

为了解决上述方案中数据量大、频繁IO查询导致的性能问题,可以采用NOSQL方案以支持水平扩展。

当前NOSQL产品有如下可选:

产品优点缺点存储示例
MOGODB高性能,支持部分筛选查询功能,海量存储不支持事务操作,数据结构json需要额外转换处理PR模型: 以json的key-value格式,维护PR的字段信息,如 { "id":"3444", "prNumber":"3030333", "applyTime":"2020-01-01 00:00:00", "applyUser":"小王", "description":"买一个15英寸苹果电脑", "money":"20000" }
HBASE高性能,支持海量存储,列动态扩展,支持行级事务查询功能有限,仅支持基于get和scan,rowkey的设计有很多限制PR模型,查询主要需求为根据pr单号或创建用户查询单据。 rowkey为 :tenantId+PR单号+申请用户id+创建时间戳 col为:创建时间、采购类型、状态、金额、需求说明、立项名称等。

动态DDL方案

每个租户的数据模型在物理上是隔离的,当遇到个性化需求时,需要动态创建DDL,该方式较重,实现成本较大。 采用此方案的,有微软的power platform、一些轻量的表单类应用产品。

总结

本文分别介绍了几种提升可扩展性的几种设计方法:大宽表(元数据驱动)、纵表、NOSQL、动态建表,这些方案各有利弊,其复杂度、扩展能力、面向的场景都不一样。

方案优点缺点使用场景
元数据模型模型通用度高,灵活性强实现成本较高、性能一般金蝶、salesforce
纵表灵活性高数据量膨胀,条件检索需求较难支持数据量不大、对检索需求不强的业务场景
NOSQL可以支持较大的数据量,性能较好条件检索需求较难支持大数据量、对性能有较高需求的场景
动态DDL具备一定的扩展性因为安全因素,不一定具备落地条件微软云平台

在实际业务建设过程中,要根据实际情况(团队技术现状、建设&维护成本、业务对扩展性灵活度的要求、性能)进行权衡,综合判断,选择适合的方案。

加入我们

扫码发现职位&投递简历

官网投递:job.toutiao.com/s/FyL7DRg