1.前言
表格存储在 2009 年阿里云成立之初即立项研发,受 Google Bigtable 的启发我们决定自研一个类似的分布式表存储。由于是基于飞天内核开发,所以架构设计上我们选择基于飞天平台提供高可扩展的分布式架构,基于盘古提供高可靠的数据存储,最重要的是会基于云来提供服务。在 2010 年发布了第一个版本,服务的第一个客户是云邮箱,支撑其海量邮件元数据存储。紧随 2011 年中阿里云官网的正式上线,在半年后的 2012 年 1 月表格存储云服务也正式上线公测。所以到今年已经算是表格存储 Tablestore(简称为 OTS)诞生的第 13 个年头,也是作为云服务上线满 10 年。
作为云服务发展的这十年,产品功能在不断完善,业务规模也在不断增长。到目前为止,Tablestore 已经在全球 24 个地域的公共云上输出,服务数千企业客户。我们也服务了集团内的众多重要 BU,包括钉钉、菜鸟、淘特、天猫、高德、盒马、蚂蚁、优酷、飞猪等。在云智能内部也是作为众多云服务的底层核心存储,包括云监控、MaxCompute、函数计算、智能存储、视频云等。当前我们服务的集群总规模超过数万台,存储数据量数百 PB。
阿里集团内的业务对产品是一个很好的锤炼,因为在这里有更复杂的场景、有更大的数据规模以及有对成本和性能更高的要求。我们也是在陪业务磨炼的这几年,锤炼了我们内核,演进了我们在某些垂直场景下的差异化功能。业务规模变大,稳定性挑战也变大,这十年也经历过几次重大故障。在存储团队内部稳定性一直是被定义为最高优先级的事,因此我们不断优化架构来提升可用性。
这篇文章接下来会先整体介绍下表格存储 Tablestore,之后会分享下在技术层面产品这几年的功能演进、技术架构演进以及稳定性优化相关的工作,以及在业务层面我们定义的核心应用场景和一些典型案例。
2.产品介绍和发展历程
2.1功能大图
以一张数据流视图的功能大图来介绍下产品,Tablestore 所提供的基础能力是分布式表存储,这个表存储有以下几个特点:
- 提供分区表模型,支持存储规模的水平扩展:数据模型参考 Bigtable 的定义,这个模型目前在开源界被归类为 WideColumn。简单理解就是一个大宽表,这个表能存储几乎不限行数的数据记录,每行数据会有一个行主键来标识唯一性。在物理分布上,表会按照行主键的范围来切分为不同的分区,通过分布式的调度这些分区来提供可扩展的能力。
- 提供 Serverless 服务形态,更简单使用:在使用上只需要开通服务就可以直接使用,无需购买 ECS 实例来部署服务。用户看到的只有数据存储和请求,底层的物理资源不可见,底层分配的物理资源随着表的存储规模和访问量变化自动弹性伸缩。
- 灵活的数据索引,加速数据的查询和检索:Bigtable 定义的表存储模型只有对行主键的索引,所以能提供非常快速的单行查询和主键范围查询。但是在实际的业务需求中,用户还需要基于非主键列进行条件查询,或者是需要支持更复杂的例如多字段组合查询或者是全文检索等实时查询和检索。所以我们在后续的功能演进中在 Bigtable 模型上进行了扩展,能够支持对表数据自动构建索引,并且提供多类索引结构(二级索引和多元索引,后文会介绍),针对不同的查询模式进行优化。
- 便捷的数据管理,管理数据生命周期和数据流转:随着数据规模的变大,数据管理会越难。Tablestore 提供更便捷的数据管理功能,包括支持 TTL 自动淘汰过期数据、支持数据的自动冷热分层来降低成本,以及通过通道服务(Tunnel Service)实现表数据的分布式 CDC(变更数据捕获) 能力让数据能实时流转。
- 完善的企业级特性,更安全的使用:Tablestore 是长在云上的,所以整个资源管理、网络和账号体系都是基于云的基础服务来构建。支持基于 RAM 的账号授权,支持基于 SLR 的服务间访问授权、支持 VPC 等专有网络以及支持基于 KMS 的 BYOK 透明加密。
2.1.1场景示意
我们看一个真实的例子来理解 Tablestore 的这些功能是怎么组合使用的,以物联网设备状态数据存储场景为例。在这个场景下需要存储数亿设备的状态数据,且每个设备会定期汇报其最新状态数据。这个场景下的表结构设计非常简单,表中每一行对应存储一个设备的数据。在 Tablestore 内只需要创建这么一张表,就能满足这个场景下的基础功能,满足数亿设备的状态更新、查询和存储的需求,且能随着设备规模的变大自动水平扩展。此时如果这个场景下需要实现更高级的功能:
- 需要实现根据多个设备状态属性做组合条件检索:只需要通过 API 对表创建一个多元索引,即可满足表内任意字段的多条件组合检索。
- 需要捕获设备的最新状态变更做一些实时计算:只需要通过 API 对表创建一个分布式 CDC 通道,即可对接 Flink 进行实时计算。
- 需要定时分析所有设备状态产出统计报表:只需要在 MaxCompute/Spark 内通过 SQL 创建一个外表关联,即可使用 MaxCompute/Spark 的计算引擎对表内数据进行全量分析。
2.1.2Data Serving
Tablestore 提供结构化数据存储服务,在应用数据这块结构化数据存储最常使用的是关系数据库,所以我们也经常会被问到与关系数据库的区别是什么。从数据应用角度来回答这个问题,关系数据库定位在 Transaction Process,而 Tablestore 定位在 Data Serving。Transaction Process 是一个基于数据 Full State 的数据处理,任何数据变更都需要保证数据状态的完整性和一致性。而 Data Serving 更强调保障数据的 online service,保证在任意规模下提供高可靠、高可用的数据服务。Transaction Process 和 Data Serving 实际是上下游的关系,通常在应用系统中数据的产生是一个 Transaction Process 的过程,而数据生成后需要提供的是 Data Serving 服务。举个实际的应用场景,在电商系统中每笔订单的生成是一个 Transaction Process 的过程,因为需要关联商品、价格、库存、账户等状态数据才能确定是否能产生订单,需要基于一个完整数据实体的 Full State 来做事务处理。而订单生成后则会转换为提供 Data Serving,此时的数据应用场景主要是查询和简单的数据更新。
2.2发展历程
在 Tablestore 发展的十余年内,在功能演进层面我们主要分为三个阶段:
- 1.0 阶段(提供稳定的表存储服务):Tablestore 的核心基础能力是提供 Serverless 表存储服务,作为云服务上线后前 5 年都是在持续打磨表引擎、稳定性以及 Serverless 产品化能力。
- 2.0 阶段(更完善的数据服务能力):满足基本的存储服务需求后,客户对数据服务能力提出了一系列更高的要求:希望支持索引,提供更灵活的数据查询和检索;希望能支持数据实时流转,进行实时计算或实时 ETL 至数仓;希望能直接通过开源计算引擎进行数据分析。因此我们在 17 年开始规划一系列重大功能特性,在 19 年的时候发布了我们的 2.0 版本,发布了通道服务(分布式 CDC)、数据索引(二级索引和多元索引)以及与计算生态的对接(MaxCompute/Spark/Flink/DLA等)。
- 3.0 阶段(安全、成本、性能、用户体验):满足基本的数据服务需求后,客户对安全、成本、性能和用户体验提出了更高的要求。我们发布了统一查询 SQL 来简化 API 调用,对接 HBR 支持数据备份、支持 BYOK 数据加密,升级了最新版表引擎内核让性能有大幅提升。3.0 阶段整体还未完成,部分功能还在不断打磨。
在形态演进层面,从一开始我们就定位为基于云提供 Serverless 的服务,随着业务的发展这个形态有所延伸。在 15 年随着专有云输出线下,我们进入了邮政、气象、国网、国税、医保、公安等项目。专有云有其特殊的服务形态,有与公共云不同的服务模式,是产品能力另一个角度的延伸。在 16 年我们开始海外输出,支持阿里云业务全球部署。
3.技术架构和核心组件
3.1整体架构
- 存储层:底层依赖盘古和 OSS,线上会部署全闪和混闪盘古集群对应当前提供的高性能和容量型两类存储规格。对于要求低成本存储低频访问的冷数据,我们会使用 OSS 来存储。
- 引擎层:目前有两个数据引擎,分别是表引擎和索引引擎。索引引擎的数据是实时同步自表引擎,对表内的数据实时构建索引。两个数据引擎间的数据同步使用 CDC 引擎,CDC 引擎能实时捕获表内的数据更新,并提供订阅的能力。
- 服务层
- 数据接口:应用访问 Tablestore 服务的统一入口,所有功能都是通过 API 对外提供。这一层的能力除了对底层管控和数据接口的封装外,还管理用户认证、权限、流控等等。
- 统一查询:使用 SQL 作为查询引擎,底层对接表引擎和索引引擎。
Tablestore 会复用大量存储内部的基础技术,包括夸父、女娲、飞天基础库等。这些基础技术在存储其他产品线不断磨炼,我们能直接享受到能力提升带来的技术红利。Tablestore 自研最核心的部分是引擎层和服务层,接下来会分别介绍下这些重要组件。
3.2分布式表引擎
表引擎是 Tablestore 的核心,数据索引、更新订阅和数据管理都是围绕表引擎构建,所以其必须具备很强的性能、很高的扩展性以及稳定性。总结下表引擎的核心技术:
- 存算分离架构:底层依赖盘古分布式文件系统,提供高可靠的数据存储。存算分离带来的优势是计算和存储可灵活扩展,分别具备灵活度很强的弹性能力,是提供 Serverless 服务的基础。
- 动态分区,自动负载均衡:分区是表具备分布式能力的基础,动态分区是表具备弹性扩展能力的基础。表的动态分区能力支持分区的自动分裂,且分裂期间将分区的不可用时间优化到百毫秒内,对用户请求几乎无影响。通过自动负载均衡灵活调度分区,自动探测并消除访问热点,保证资源均衡减少访问抖动。
- 高性能数据引擎:表引擎内部使用了大量技术来优化读写性能,包括用户态通信框架(Luna)、线程切换和内存拷贝优化、无锁数据结构、行列混存数据块优化等等,对比同类开源引擎有数倍的性能提升。
- 高可用,稳定性优化:影响可用性的场景主要包括突发流量、负载不均衡以及 Worker 节点的 Failover。我们通过全局流控和单机流控结合来处理突发流量,利用动态负载均衡来均衡资源和消除访问热点,优化 Worker 节点的 Failover 时间降低影响,目前单集群的可用性已经能达到 99.99%。另外历年的故障和越来越复杂环境下的线上问题也让我们看到并解决了很多 corner case 问题,对稳定性提升也有很大的帮助。
3.3分布式 CDC 引擎
通道服务是 Tablestore 提供的 CDC 能力,CDC 的全称是 Change Data Capture,提供实时捕获表内数据更新的能力。有很多数据库产品都提供类似能力,比如 MySQL Binlog、DynamoDB Stream 以及 CosmosDB Change Feed 等。可以说 CDC 是现代数据系统中核心数据组件必备的能力之一,因为它有几个非常关键的用途:
- 异构存储实时同步:现代应用架构中包含很多异构存储组件,例如数据库、缓存、搜索引擎、消息队列等,数据会在这些组件间实时流转。如何保证这些异构组件间的数据一致性是一个技术难点,一个比较好的架构实践是选取唯一的 DataSource,作为整个系统中数据的『Single source of truth』,通过捕获这个 DataSource 的变化来实现实时同步。
- 事件驱动架构:事件驱动是一个很好的解耦应用系统的最佳架构实践,很多包含多种微服务的复杂应用系统应用了事件驱动技术,例如不同服务间通过消息队列来实时异步的传递消息从而能实现完全的解耦。应用与应用间通过消息队列来实现事件驱动,而数据存储与应用间需要通过 CDC 技术来实现事件驱动。
- 实时计算:在很多应用场景中需要对数据进行实时统计,例如双十一大屏。计算实时性依赖于数据的流转速度,存储与实时计算的对接需要依赖 CDC 技术来提升流转速度。
上图是 Tablestore 通道服务的架构,表由多个分区构成,每个分区会包含一个 Commit Log 队列,队列内按照数据写入顺序保存了该分区内的所有数据更新记录。如果发生分区分裂,老分区的队列会停止写入,分裂出来的两个新分区会写入各自的新的队列。通道服务本质上开放了分区内 Log 队列的数据查询,但是仅仅开放查询接口还不够,通道服务内实现了以下几个关键技术:
- 全增量一体:异构存储间需要数据实时同步并且保持数据最终一致,则需要先同步当前全量数据后在同步增量。Log 队列内不会保存全量数据,只保存最近一段时间内的数据,所以如果要同步全量则需要直接读表。通道服务底层封装了全量和增量数据的查询,对外暴露一致的接口,大大简化了使用。
- 分布式保序消费:保证数据一致的关键是能够严格按照数据变更记录顺序进行回放,如果仅查询一个队列,保序是比较好实现的。但是由于分区会动态分裂产生新的分区,所以会导致同一行数据的变更记录可能会存在不同的队列中。老分区和新分区间形成父子关系,要实现保序就得保证先消费父分区再消费子分区。在通道服务内部管理了分区间的父子关系,并且能够严格保证父子分区的消费顺序。
- 消费状态管理:通道服务管理了数据生产端以及能够保证消费行为的正确性(例如保序消费),但是由于消费端是托管在客户应用内,所以很难管理消费端的运行情况。如果消费端出现异常,会导致数据消费滞后。为此通道服务在服务端提供消费状态管理,消费端会实时上报状态和消费统计。利用此状态数据,可以快速定位消费滞后的异常节点。
3.4分布式索引引擎
主表提供基础的主键索引,仅能提供简单的根据主键查询或者是主键范围查询。但是很多场景下,需要根据非主键列做条件查询,此时需要构建索引来加速。索引可以是内置也可以外置,也可以使用内外组合的方案。例如 MySQL 提供联合索引能满足绝大部分场景下的查询加速,这是纯内置的方案。但是联合索引使用上会有局限,要求查询条件必须符合索引列的最左匹配原则,所以在查询条件需要灵活组合的场景不得不选择用 Elasticsearch 作为外置索引。这就是一个典型的内外组合的场景,使用是非常常见的。内置索引的好处是能够比较简单管理表与索引的一致性,也能更好的保证实时同步。外置索引虽然灵活,但是在处理数据一致性、以及优化数据同步延迟上有非常大的挑战。早期在 Tablestore 没有提供内置索引时,用户大多数选择的是外置索引的方案,遇到了同样的问题。所以我们决定研发我们的内置索引,并且同时提供类 MySQL 的联合索引和类 Elasticsearch 的倒排索引,满足绝大部分场景的需求。
Tablestore 提供的类 MySQL 联合索引的能力叫二级索引,提供分区内强一致的 LocalIndex 以及全局最终一致的 GlobalIndex。二级索引的数据分布和索引结构与主键索引的实现是一致的,逻辑上看二级索引就是另外一张表,该表是对主表数据的另一个维度的重新排序。二级索引的功能由表引擎直接提供,这一章节主要介绍另一个类 Elasticsearch 能力的多元索引的架构。
上图是多元索引的架构,总结下多元索引的核心技术:
- 存算分离架构:底层也是依赖盘古分布式文件系统,多元索引所需要动态调配的计算资源会更多,所以更需要依赖存算分离技术。
- 可扩展的查询能力:多元索引的数据分区能提供多个读副本,这是与表引擎的数据分区很不一样的地方,因为多元索引主要为查询做优化。读副本支持动态扩展,当探测到查询负载过高时系统能够自动扩展读副本,依赖于共享存储架构能够在秒级别完成。
- 数据组织优化:
- 哈希分区:由于多元索引提供的是不确定的多字段条件组合查询,所以很多场景下无法筛选结果数据所在分区,通常都是一个全分区的联合查询,内部采取 scatter-gather 的执行模式。为避免有分区查询长尾,所以分区间尽量数据均衡,因此多元索引的数据分区采取的是 Hash Partition 的策略,区别于表的 Range 策略。
- 路由键:某些场景下查询条件中会带固定的条件字段,例如订单索引表的查询通常是会给定 userid 条件,如果能根据固定条件提前做分区筛选,那查询效率能更高。所以我们提供了 Routing Key 的策略,与 Hash Partition 组合使用,如果查询条件中给定了 routing key 的值,则能提前做分区筛选。
- 预排序:如果数据排序与查询结果顺序一致,则可以无需查询出所有结果后进行全排序,只需要获取到满足条数的结果即可直接返回,对查询效率是一个很大的提升。所以多元索引提供了预排序的能力,如果查询条件总是按某个顺序返回结果,则可以定义预排序来进行优化。
- 多类型索引结构:索引结构有多种,例如 B-tree、Hash、Bitmap、BKD-tree、Reverted Index 等,每种索引结构针对特定的查询模式优化。针对我们常见的查询场景,多元索引实现了优化数值范围查询的 BKD-tree 索引以及优化多条件组合检索的 Reverted Index。
- 自适应优化和索引重建:上面提到的数据组织优化中路由键和预排序策略都是需要根据实际的查询模式来决定的,很多业务在一开始设计索引时无法提前考虑到所有的优化策略。所以多元索引提供了自适应的优化,会自动分析常见的查询模式来决定是否采取路由键和预排序优化措施,并且自动重建索引,对上层使用是完全透明的。
- 动态索引列和灰度切换策略:很多应用场景在上线后会有新增索引列的需求,多元索引提供了两种添加索引列的方式。如果需要存量数据重建索引,我们提供了灰度切换策略,新索引会在后台重建索引,索引数据追平后可以进行查询流量灰度,如果没问题可以操作切换或者回切,保证新索引能安全上线。如果不需要对存量数据进行索引重建,则新增索引列可以仅对新数据生效。
3.5统一查询引擎
最早 Tablestore 仅提供 API 的访问方式,每个 API 对应底层每个基础模块的原子功能。纯 API 的好处是用户能比较灵活的组合调用底层功能,具备很强的 DIY 能力,但是不便之处有很多:
- 应用层代码逻辑极其复杂:一个简单的单行查询用 SQL 来表达的话主体逻辑只需要一行代码,但是裸调 API 的形式至少需要数倍代码量。如果是调用多元索引的统计聚合,复杂的 Group By 加多层聚合代码量甚至能达到几十倍。
- 在查询时无法附带一些简单的数据处理:通常从存储层查询出结果数据后,某些场景下不需要返回原始结果,而是需要一些统计聚合结果,例如 Sum 和 Avg 等。裸 API 无法表达计算处理,有了 SQL 后就能很容易支持一些轻量级的统计聚合。
- 很难高效的自主选择索引和反查主表:索引选择依赖一些 RBO 或 CBO 优化算法,应用层勉强可以自己实现几个简单的 RBO,但是很难进行 CBO 优化。另外索引选择优化算法最好是服务内集成,这样不需要每个应用单独实现一套。另外如果要查询索引后反查主表,需要分别调用索引的查询 API 后再调用主表的 API,实现起来非常复杂。
- 无法提供交互式的查询:代码裸调 API 很难实现交互式的查询逻辑,通常需要使用 SQL 来支持。
- 应用组合 MySQL 使用或从 MySQL 迁移过来,需要很大的改造代价:很多场景是把 Tablestore 作为 MySQL 的分层存储历史数据,由于表结构是大体相同的,所以查询逻辑也是大体相同的。如果不支持 SQL,应用层需要很大的查询代码改造代价来适配 API。
上述都是来自真实的场景和客户真实的反馈,所以我们考虑良久在 Tablestore 3.0 的规划中决定引入 SQL 查询引擎。但是引入 SQL 前,在架构设计上我们要想清楚几个关键点:
- SQL 提供的是关系模型还是联邦查询?我们 SQL 的定位是做联邦查询而不是表达独立的模型,主要有两点:一是 Tablestore 的底层模型能表达更丰富的语义不完全与关系模型兼容;二是底层多种数据模型间需要有数据联合查询的需求。
- API 和 SQL 的关系是什么?上面一个定位搞明白了,SQL 和 API 的关系也清楚了,SQL 会基于 API 来构建。或者更精确的说 API 定义底层数据模型抽象,SQL 基于此在上层再做一层抽象。
- Data Serving 场景对算力的要求是什么?具体到技术指标,我们希望提供高并发、高 QPS 和低延迟的查询,要求不同 SQL 执行引擎架构选择和优化目标也会不同。
- SQL 引擎优化目标是什么?基于上面的目标,在 Data Serving 场景我们主要优化的目标是:一是提供可水平扩展的 SQL 执行引擎;二是优化单位算力下的性能,而不是如何调度更多算力。
以上就是 SQL 引擎的架构,它的核心技术点包括:
- 提供联邦查询能力:基于底层存储引擎提供的 API 来构建,实现 API 写入的数据能被 SQL 查询。
- 自适应索引选择和主表反查:根据 RBO 和 CBO 优化索引选择,支持自动反查主表。
- 可水平扩展的执行引擎:与 API 层类似架构,采用无状态对等节点,可水平扩展支持更高 QPS。
- 单位算力下的性能优化:做了一系列优化来优化单位算力下的计算性能,包括:1. 与存储层的数据交换协议优化;2. 更多计算下推到存储层执行;3. 计算引擎的输入数据采取列编码,以应用向量化执行技术来优化性能。
4.核心应用场景
这一章节会给大家介绍下 Tablestore 的几个核心的应用场景,我们对『核心』场景的定义是:
- 在该场景下 Tablestore 在内核能力方面具备一定的竞争力,例如成本、性能和规模等。
- 有已有的大规模、成熟、高要求业务稳定运行多年,并经过大流量的考验。
- 在该场景下 Tablestore 具备一定的差异化特性,对应用架构带来很大的收益。
4.1应用场景分类
我们对 Tablestore 上的应用场景架构类型上做了分类,大概包含以上这么几类:
- 互联网应用架构
- 数据库分层架构:对于需要 Transaction Process + Data Serving 的场景,会采取分层的架构,典型应用是订单存储。
- 分布式表存储架构:对于仅需 Data Serving 的场景,对存储层的高可扩展和高并发访问要求很高,会直接基于 Tablestore 来构建,典型应用是 IM/Feeds 消息存储、网盘元数据存储、健康码存储等。
- 数据湖架构:Bigtable 的典型应用是在大数据架构中作为实时的结构化数据存储,数据湖的一个特征是大数据架构各组件的模块化和 Serverless 化。Tablestore 在这个架构下可以作为流批计算引擎的源表、维表和结果表,提供 Serverless 结构化数据存储的能力。
- 物联网架构:物联网是一个偏垂直的业务场景,由于其需要管理海量设备,所以需要一个可扩展的存储。在具体的应用场景下,Tablestore 可以作为设备消息、设备元数据和设备时序数据的存储。
数据类产品是百花齐放的,开发者往往最头疼的是如何做选型,在选型层面我们的经验是:
- 检验一个产品是否满足需求的最佳方式是实测,且不仅是简单的功能测试,还需要进行规模、性能层面的测试。
- 看下有没有和你相似业务的应用架构可借鉴,当然可借鉴的前提是该业务已经实现了一个更优秀的架构,且已经稳定运行多年,抗住了大流量的考验。
- 不用怕选型错,但要保留可替换组件的能力,实现一个灵活的架构。
- 目前的水平来说,分布式系统还很难实现业务自运维,所以选型时也要考虑使用的产品的背后是否有一个稳定的技术支持团队。
4.2订单存储架构
订单存储是典型的数据分层架构的应用场景,在 2020 年我们协助阿里云财账系统进行存储层改造,目标是改造后能支撑未来 5-10 年规模增长,与 TP 数据库分离解决账单检索和导出时对查询的影响,典型的 Transaction Process 与 Data Serving 分离。在一期总共完成全量 400 亿条账单数据迁移,慢接口平均延迟从 7s 降低到 100ms,账单检索性能提升 55.3 倍,大用户账单导出速度提升 35 倍。之后我们对该存储架构进行了总结,在集团内外做了复制,也吸引了更多订单类业务的存储改造。一期目标完成后,在后续的二期三期累积数据规模已达到了万亿。
我们对此分层存储架构做了技术总结,可以继续阅读下公众号的『云上应用系统数据存储架构演进』这篇文章。此外我们对该大规模订单存储场景也总结了架构技术分享,可以继续阅读『基于 MySQL + Tablestore 分层存储架构的大规模订单系统实践-架构篇』这篇文章。
4.3元数据存储架构
元数据从类型上看更像状态数据,通常需要有事务处理。但是某些场景下,元数据层首先需要有横向扩展的能力,且对事务的要求会稍弱,不需要全局事务仅需局部事务。阿里内部有几类业务使用了 Tablestore 作为元数据存储,主要是网盘元数据、媒资元数据和大数据元数据。我们作为 PDS 服务的底层存储,间接支撑了阿里内部多个个人网盘业务。作为 MaxCompute 和 DLF 的底层元数据存储,支撑了阿里内外大数据场景下的元数据存储。
4.4消息存储架构
消息存储典型的场景主要是互联网场景下的 IM 和 Feeds 流,以及物联网场景下的设备消息。阿里内部最重量级的消息应用就是钉钉、手淘和应用消息系统 ACCS/Agoo,我们从 16 年开始与钉钉合作,20 年一起经历了疫情的洪峰,21 年在钉钉场景首先应用了最新版本的表引擎,大大提升了性能和稳定性。钉钉基于 DingTalk 的消息底座推出了 IMPaaS 平台,统一了手淘消息和其他消息业务。Tablestore 作为 DingTalk 和 IMPaaS 的底层消息存储,支撑了集团内的各个消息业务。
我们总结了在 IM 和 Feeds 场景使用 Tablestore 的技术架构分享,可以继续阅读『揭秘!现代IM系统的消息架构如何设计?』、『如何造一个“钉钉”?谈谈消息系统架构的实现』和『亿级规模的 Feed 流系统,如何轻松设计?』这三篇文章。钉钉也将自己沉淀多年的消息系统架构实现做了分享,可以阅读『5亿用户如何高效沟通?钉钉首次对外揭秘即时消息服务DTIM』这篇文章。
4.5大数据存储架构
传统大数据架构是 on-premis 的部署模式,搬到云上后演变为数据湖架构,各组件逐步 Serverless 化,最典型的如 OSS 替换 HDFS。传统大数据有个经典架构体系为 Lambda,该架构下要求数据分别写入批存储和流存储,分别用于批计算和流计算。后续有一些新的架构对 Lambda 进行简化,典型的如 Kappa 和 Kappa+。两套存储和计算引擎带来的是维护的复杂度,所以后续技术往流批一体进行演进。流批一体又分为计算层的流批一体例如 Flink 和 Spark,以及存储层的流批一体。Tablestore 提供批量查询和基于 CDC 的流查询,所以我们决定我们可以做流批一体的存储,所以我们尝试对外输出了 Lambda Plus 架构,也吸引了部分业务基于此架构进行改造。
对于 Lambda plus 架构的介绍,可以阅读『大数据架构如何做到流批一体?』这篇文章。关于 Tablestore 作为结构化数据存储在大数据架构中的定位,可以继续阅读『结构化数据存储,如何设计才能满足需求?』这篇文章。在大数据架构中作为计算引擎的维表和结果表,Tablestore 具备 serverless 和大规模的优势,我们有一个详细的对比,可以继续阅读『云原生大数据架构中实时计算维表和结果表的选型实践』这篇文章。
4.6物联网存储架构
物联网场景下主要使用 Tablestore 来存储设备元数据、设备消息数据和设备时序数据,其中的核心链路是元数据和消息数据存储,其架构与上述互联网场景是略同的,在互联网场景我们积累了非常多的元数据和消息数据存储的经验,所以也能很好的应用在物联网场景。时序数据是一类特殊的数据,其有一些明显的特征,例如数据都会带时间戳,对于热数据主要是实时计算,对于冷数据主要是批量查询。时序数据的规模较大,且有非常明显的冷热之分,所以通常需要用到冷热分层技术来降低成本。我们看到时序数据场景能做非常多的特殊优化,所以在 Tablestore 3.0 中开发了针对时序数据存储优化的 IoTStore,关于 IoTStore 的使用场景介绍可以继续阅读『一站式物联网存储解决方案』这篇文章。
5.最后
感谢大家的阅读,希望大家对 Tablestore 的整个演进历程能有更多的了解。我们还有很长的路要走,上文中提到的整体架构中的各个组件,在成本、性能、稳定性等方面都还有提升空间,我们会继续努力追求极致。
Tablestore 能坚持走到现在,除了因为核心研发团队的坚守,更重要的是因为客户的信任和支持,感谢所有在背后支持 Tablestore 的客户们。