白皮书下载地址:https://rockset.com/Rockset_Concepts_Design_Architecture.pdf
Rockset 是一家美国软件公司,成立于2016年,创始人是在Facebook、雅虎、谷歌、甲骨文和VMware等公司积累了大规模数据管理和分布式系统经验的软件工程师, 其产品 Rockset 是一个实时分析数据库,可帮助开发人员构建数据驱动型应用,同时消除其他普通数据基础设施固有的复杂性。
Rockset 是一种基于云的实时分析数据库,旨在实现对海量半结构化数据的查询,同时消除运维负担。Rockset 采用无服务器和完全托管的方式, 可卸载管理配置、集群调配、反范式化(denormalization)和分片/索引管理等工作。
定位
Rockset is the search and analytics database built for the cloud, with real-time indexing and full-featured SQL on JSON, time series, geospatial and vector data.
这是 Rockset 自己的 slogan,重点关键词:
- Analytics
- Cloud
- Real-Time
- SQL
数据模型
Rockset 使用文档模型来组织数据,传统的关系数据库需要用户提前定义 schema,但对半结构化数据(semi-structured,比如:JSON、Avro、Parquet等)不友好,文档数据库可以极大方便用户的使用,用户的数据即使发生变化,也不需要去关心表的元数据,即 schemaless。
与其他文档数据库不同的是,Rockset 支持用 SQL 作为查询语言,支持 join。
云原生的架构
为了充分利用云上的弹性资源,
- 使用共享存储(比如:S3)而不是 shared-nothing 的存储,共享存储可以让存储与计算分离,便于对不同组件进行扩缩容
- 持久性与性能分离。传统的数据库一般采用多副本的方式来保证高可用,劣势就是浪费机器。通过使用价格更低廉的云对象存储来保证高持久性,并只在需要时才增加副本,用来辅助查询,性价比更高。
- 多级存储的能力。热数据放在 SSD,冷数据放在对象存储
RocksDB-Cloud
RocksDB-Cloud 是 Rockset 的嵌入式持久化存储引擎,扩展定制(Rockset CTO Dhruba 之前在 Facebook 是 RocksDB 初始工程师,founding engineer)业界广泛使用的 RocksDB,支持以下额外功能:
- 将数据库数据和元数据持续自动复制到云存储。在一台机器宕机时,可以自动申请另一个 EC2 实例,并打开之前的数据。
具体来说,RocksDB-Cloud 是这么工作的:
- 当有新的 SST 生成时,RocksDB-Cloud 会把它上传到 S3,以确保持久性。然后,热存储层(通常是 SSD)从 S3 抓取文件,以提高性能。文件是不可变的,这简化了热存储层的角色:它只需要发现和存储新建的 SST 文件,并淘汰旧的 SST 文件。 在执行查询时,RocksDB-Cloud 会从热存储层请求数据块,每个数据块在文件中以偏移量和大小表示。而且还会在计算节点中缓存最近访问过的数据块,以便快速检索。
- 除了数据文件之外,RocksDB 还会在 MANIFEST 文件中存储元数据信息,以跟踪代表当前数据库版本的数据文件。这些元数据文件在每个数据库实例中的数量是固定的,而且体积很小。元数据文件是可变的,会在创建新的 SST 文件时进行更新,但很少被读取,也不会在执行查询时被读取。 与 SST 文件不同的是,元数据文件本地存储在计算节点和 S3 中以保证持久性,但不存储在热存储层中。由于元数据文件很小,而且很少从 S3 中读取,因此将它们存储在计算节点上不会影响可扩展性或性能。此外,这还简化了存储层,因为它只需支持不可变文件。
每个RocksDB-Cloud 中的实例都是可以复制的,它采用一种称为 zero-copy-clone 的技术,可以让一台新集群复制已有的数据库,两个实例可以同时运行,底层共享相同的数据文件。
计算与存储分离
RocksDB-Cloud 允许将计算和存储分离开来。RocksDB 的所有持久性数据都存储在云存储中的 SST(排序字符串表)文件集合中。
我们会执行一个合并过程,将这组 SST 文件合并起来,生成新的 SST 文件,并清除覆盖或删除的键。压缩是一个计算密集型过程。传统上,压缩也会在托管存储的服务器本地 CPU 上进行。不过,由于 SST 文件创建后不会被修改,因此我们可以将压缩计算与存储分离开来。这可以通过使用远程压缩来实现,一旦收到合并请求,合并任务就会被卸载到无状态的 RocksDB-Cloud 服务器上。这些远程合并服务器可以根据系统的负载情况进行自动扩展。
计算与计算分离
在 Rockset 中,计算-存储分离是计算-计算分离的前身,多个独立的计算单元可以同时对相同的共享数据进行读写,甚至可以是实时写入的数据。
为了实现计算与计算的分离,Rockset 在设计上将执行流式数据写入任务的模块与执行查询处理的模块完全分开。因此,Rockset 中的数据写入、转换和索引代码路径与查询解析、优化和执行完全独立。
Rockset 会将 RocksDB leader 中的 memtable 复制到查询数据的 follower 实例的 memtable 中。这样,所有 follower 实例都能在个位数毫秒内获得新数据。这种实现方式也只需在 leader 上进行一次索引和压缩等计算密集型的写入工作,从而避免了 follower 的冗余计算费用。
在存算分离、算算分离后,提高查询的关键点是如何构建缓存,下面文章有相关介绍,后续有时间整理到本文中:
分片与复制
Rockset 中的数据是横向分割的。Rockset 数据集中的每个文档都被映射到一个抽象实体,称为 "微分片"(microshard)。这种映射是通过微分区映射函数来完成的,该函数是输入为文档 ID 的函数。一组微分片组成一个Rockset分片。基于文档的分片使系统更容易横向扩展。每个分片都与 RocksDB 实例一一对应,后者以按词典排序的键值对形式保存索引数据。
将大型数据集中的数据拆分成多个分片,可以利用分片级并行性,对每个分片进行并行扫描,从而帮助更快地对此类数据集进行查询,而不会让扫描成为瓶颈。Rockset 允许你对同一分片进行多个复制,以提高可用性。
融合索引(Converged Indexing)
Rockset 将所有接收到的数据存储在融合索引(Converged Index)中,这是最有效的数据组织方式。它的灵感来自搜索和列式索引。
上图的示例使用了两个简化文档,其中只有一个字段 "name"。在右侧,你可以看到 Rockset 为这两个文档生成并存储的所有键值对。Rockset 会从每个文档中生成许多键值对,因为它会自动将数据存储在多种类型的索引中。
前两个键值对来自行索引。请注意键是如何构造的。我们在键中使用 "R "来表示 RowStore,并使用文档 ID(0,1),然后是列(名称)。这种方法让我们可以将特定文档的所有值存储在一起,就像在任何行存储中一样。行索引使我们的查找延迟非常低。
接下来的两个键值对来自列索引,其中的键组件被翻转。我们使用 "C "来表示列存储(ColumnStore),然后使用列(名称)和文档 ID(0,1)。我们将某一特定列的所有值存储在一起,这样可以加快扫描时间,并获得更好的压缩效果。
最后,对于搜索索引,我们实际上是将值放入键中,并将文档 ID 作为后缀存储。我们使用 "S "来表示搜索索引,然后是列(名称)、值(德鲁巴、伊戈尔),最后是文档 ID(0,1)。因此,举例来说,如果您要查找 name = Dhruba 的所有文档,那么就可以通过前缀 S.name.Dhruba 快速找到键值存储中的所有键。
在查询时,Rockset 可以自动根据查询类型,选择最佳的索引。比如:
|
|
因为这个查询没有过滤条件,因此优化器将会选择 ColumnStore。因为列存储将列分开,因此该查询只需扫描列关键字的值,性能比传统的行存储快得多。
|
|
优化器将使用数据库统计信息来确定该查询只需获取数据库的一小部分内容。它将决定使用搜索索引来回答查询。
实时索引数据
融合索引是一种实时索引,可与多个数据源保持同步,并在一秒钟内反映新数据。它是一个覆盖索引(covering index),这意味着它不需要反馈到数据源就可以提供任何查询服务。这对于可预测地提供高性能至关重要。
传统上,为数据库维护实时索引是一项昂贵的操作,但 Rockset 采用了现代的云原生方法,通过分层存储和分解系统设计实现了大规模高效维护。从概念上讲,我们的逻辑倒排索引与其作为键值存储的物理表示是分离的,这与其他搜索索引机制截然不同。我们还通过在键值之间使用 delta 编码和在每个文件中使用字典编码的 zSTD 压缩技术,使聚合索引具有很高的空间效率。此外,我们还使用 Bloom 过滤器来快速查找键–我们使用的是 10 位 Bloom,这样可以减少 99% 的 I/O。
我们的索引是完全可变的,因为每个键都指向一个文档片段–这意味着用户可以更新文档中的一个字段,而不会触发整个文档的重新索引。传统的搜索索引往往会遭遇重新索引风暴,因为即使 20K 文档中 100 字节的字段被更新,也会被迫重新索引整个文档。
查询流程
Rockset 提供完整的 SQL 接口来查询数据,包括过滤器、聚合和连接。当一个查询进入 Rockset API 服务器时,它会被路由到聚合器。聚合器对来自数据叶的数据进行低延迟聚合,包括列聚合、连接、相关性排序或分组。 概括地说,SQL 查询在 Rockset 中要经历 3 个主要阶段(和传统数据库类似):
- 规划(Planning)
- 优化(Optimization)
- 执行(Execution)
聚合器对查询进行规划,并将规划中的各个片段路由到持有数据的适当分区,以便为该查询提供服务。查询结果会返回到聚合器,聚合器再将结果发送回 API 服务器。我们引入了更多层次的聚合器,以便在多个聚合器之间分配处理可能使单个聚合器成为内存/计算瓶颈的查询。
总结
Rockset 这种面向云原生架构是现在设计分布式系统的主流思路,可能什么新奇之处。借助于相对廉价的对象存储,用户可以节约成本, 并且可以相对容易实现存算分离。但像 Rockset 这种有能力修改 RocksDB 源码,使之运行在云原生的架构下怕是没有几个。
可以好不夸张说,RocksDB Is Eating the Database World,它极大地减轻了数据库存储引擎的开发时间,而 Rockset 将它搬到了云上, 这无异于如虎添翼:
- RocksDB 的出现,使得开发人员能够在更短的时间内构建更高性能、更可靠的数据库存储引擎。
- 而 Rockset 将 RocksDB 搬到云上,则为用户提供了更加灵活、可扩展、低成本的数据库解决方案。
Rockset 核心源码已经在 GitHub 公开,感兴趣的读者可以自行查阅。