FoundationDB Record Layer: A Mul
FoundationDB Record Layer: A Multi-Tenant Structured Datastore 是苹果公司在 SIGMOD 2019 上发表的一篇论文,介绍他们在 FoundationDB 之上建立的一层结构化存储。
FoundationDB 和 FoundationDB Record Layer 的代码都在 Github 上开源了。
TL;DR
- FoundationDB 是苹果公司开源的一个分布式 KV,支持水平扩展,高可用和可串行化隔离级别的分布式事务。
- FoundationDB Record Layer 是建立在 FoundationDB 之上的类关系模型的结构化数据层,支持多租户,以 library 方式提供给应用。
- Foundation Record Layer 并没有实现 SQL。论文提到,以后可能在 FoundationDB Record Layer 之上实现 SQL。
设计目标
FoundationDB Record Layer 的目标是:
- 支持水平扩展(horizontal scalability)
- 高可用(high availability)
- 分布式事务( distributed transaction)
- 多租户(multi-tenancy)
- 类关系模型的结构化存储(structured data storage)
前三点其实都由 FoundationDB 实现了。所以,FoundationDB Record Layer 的重点是实现多租户和类关系模型。
整体架构和多租户
FoundationDB Record Layer 的架构(图片来自论文)FoundationDB Record Layer 在设计上是完全无状态的(stateless),并且是以 library 的方式直接链接到应用程序中。
Providing isolation between record stores is key for multi-tenancy.
On the data level, the keys of each record store start with a unique binary prefix which defines a FoundationDB subspace.
FoundationDB Record Layer 的租户粒度是 logic database,也叫 record store,其实就是一段逻辑上连续(前缀一样)的 key-value。
如上图所示,FoundationDB Record Layer 的整体架构:
- 应用程序(Application)通过 library 的方式使用 FoundationDB Record Layer。
- 一个 record store 包含 record、indexes 和 metadata,它们都被编码成一段连续(前缀一样)的 key-value,存储在 FoundationDB。
- MetaData Store 主要存储的是 schema 的定义,可以保存到 FoundationDB 或者其他存储。大部分情况下,这些数据都会被 cache 在 client。
论文中举了一个使用 FoundationDB Record Layer 的例子:苹果公司内部的 CloudKit 框架。
CloudKit 使用 FoundationDB Record Layer 管理数据:一个用户(User)的一个应用(Application)的数据被组织成一个 record store(租户)。所以,CloudKit 使用 FoundationDB Record Layer 维护的租户数量为:用户数 * 应用数,数量上大概在数十亿级别。
类关系模型
这一块的内容,介绍了 schema 的定义和维护,重点内容是和索引相关的,包括 FoundationDB Record Layer 实现的几种索引类型和提供给应用自定义索引的能力。
Schema 的定义和维护
Records in a record store are Protocol Buffer messages and a record type is defined with a Protocol Buffer definition.
FoundationDB Record Layer 通过 Protocol Buffer message 来定义 schema,同时支持多种内置的索引类型。为了适应各种业务需求,FoundationDB Record Layer 在设计上提供了一定的扩展性,应用可以通过类似插件的方式实现自己的索引类型。
在 FoundationDB Record Layer 里,“索引(index)”是一个更宽泛的概念。我们一般提到数据库索引,在 FoundationDB Record Layer 这里被成为 VALUE indexes。
为了实现各种类型的“索引”,FoundationDB Record Layer 引入了两个概念:key expression 和 index maintanance。
key expressions
Indexes are defined by an index type and a key expression which defines a function from a record to one or more tuples consumed by the index maintainer and used to form the index key. The Record Layer includes a variety of key expressions and also allows clients to define custom ones.
Index maintenance
Defining an index type requires implementing an index maintainer tasked with updating the index when records change.
简单来说,key expression 就是一个根据 record 生成 index-key 的生成器。而 index maintanance 是用来当 record 发生变化时维护对应的索引。
这两者类似一个拦截器,拦截每一个 record 的变化信息,生成相应的 key-value。只要经过 key expression 和 index maintanance 生成的 key-value,在 FoundationDB Record Layer 里都被称为“索引”。
Online index building
在一个 schema 新增一个索引之后,需要在线对旧的 record 生成对应的索引。
在线生成索引的逻辑分三步执行:
- 将索引的状态设置为"只写"。此时,所有前台的写操作(insert/update/delete)都会更新相应的索引。
- 后台任务异步更新旧记录的索引。
- 异步更新旧记录的索引完成之后,将索引设置为“读写”。
关于分布式数据库的在线 schema 变更,推荐一篇更详细的论文:Online, Asynchronous Schema Change in F1。
索引类型
FoundationDB Record Layer 内部实现了 5 中类型的索引:VALUE Indexes,Atomic Mutation Indexes, VERSION Indexes, RANK Indexes 和 TEXT Indexs。此外,应该可以通过 FoundationDB Record Layer 提供的扩展机制实现自己的“特殊索引”。
VALUE Indexes
The default VALUE index type provides a standard mapping from index entry (a single field or a combination of field values) to record primary key.
其实 Value Indexes 就是我们日常经常提到的“索引”、“唯一索引”:(field0, field1 ... fileldN) -> (primary key)
Atomic Mutation Indexes
Atomic mutation indexes are implemented using FoundationDB's atomic mutations.
These indexes are generally used to support aggregate statistics and queries.
简单说,所谓“原子更新索引”,其实就是存储多了一个/多个 key -> 统计信息 的数据,这些信息使用了 FoundationDB 提供的原子更新能力来维护,用来支持简单的聚合查询。这些 key 可能成为热点数据,进一步成为系统性能的瓶颈。
FoundationDB Record Layer 支持的 Atomic Mutation Indexes 有:
- COUNT - number of records
- COUNT UPDATE - times of field has been updated
- COUNT NOT NULL - records where a field isn't null
- SUM - summation of a field's value across all records
- MAX (MIN) EVER - max(min) value ever assigned to a field, over all records, since the index has been created
VERSION Indexes
VERSION indexes are similar to VALUE indexes in that each has an index entry that maps to a primary key. However, a VERSION index allows the index's key expression to include a special "version" field...
这类索引和 VALUE indexes(普通的索引、唯一索引)类似,不过索引上会增加多一个版本号。这个版本号是单调递增的。VERSION Indexes 向应用层暴露了 FoundationDB 集群的事务执行顺序。应用可以利用这个来实现 change-tracking。
RANK Indexes
The RANK index type provides efficient access to records by their ordinal rank (according to some key expression) and conversely to determine the rank of a field's value.
RANK indexes 是用来高效实现类似"第 k 个"这种查询的。
TEXT Indexes
The TEXT index enables full-text queries on the contents of string fields。
字符串类型字段的全文索引。