云原生的分布式 SQL 数据库-YugaByte DB
2021-11-27 21:26:10 阿炯

YugaByte DB 是一个高性能、云原生的分布式 SQL 数据库,采用C/C++开发并在ApacheV2开源许可证下授权。


The Resilient Distributed SQL Database


100% open source. Designed to run anywhere your cloud native apps take you.

YugabyteDB is a high-performance, cloud-native distributed SQL database that aims to support all PostgreSQL features. It is best to fit for cloud-native OLTP (i.e. real-time, business-critical) applications that need absolute data correctness and require at least one of the following: scalability, high tolerance to failures, or globally-distributed deployments.


Features

Powerful RDBMS capabilities
Distributed transactions
Continuous availability
Horizontal scalability
Geo-distributed, multi-cloud
Multi API design
100% open source

值得关注的特性包括:

可插入式的查询层,支持两个分布式 SQL APIs:
    YugaByte SQL (YSQL) - PostgreSQL 兼容的关系型 API
    YugaByte Cloud QL (YCQL) - 半关系型的类 SQL API ,支持文档/索引和 Apache Cassandra QL roots
基于Google Spanner设计的自动分片,共识复制和分布式事务架构
提供水平伸缩能力、强一致性以及高可用性
极具弹性 - 可自动容忍磁盘,节点,区域和区域故障
支持地理分布式部署(多区域,多区域,多云)
可以部署在公共云,也可以部署在 Kubernetes 内部。


整体架构(Architecture):


查询层结构(Logical Architecture):


四个节点的 YugaByte DB 的结构:


YugaByte DB 的存储层是基于 RocksDB 的,结构如下:


它与CockroachDB非常的相似,不同的是它是C++而不是Go语言所开发且授权协议还没有变更。

与国内的 TiDB 和国外的 CockroachDB 类似,也是受到 Spanner 论文启发,所以在很多地方这几个数据库存在不少相似之处。Yugabyte 也主打全球分布式的事务数据库——不仅能把节点部署到全球各地,还能完整支持 ACID 事务,这是其最大的卖点。除此以外还有一些独特的特性,比如支持文档数据库接口。Yugabyte 早期被设计成一个文档数据库,后来才调整技术路线开始主打 SQL 接口。来看看EricFu对其的入门分析。


Yugabyte 采用两层架构:查询层和存储层。不过这个架构仅仅是逻辑上的,部署结构中,这两层都位于 TServer 进程中,这点和 TiDB 不同。

Yugabyte 的查询层支持同时 SQL 和 CQL 两种 API,其中 CQL 是兼容 Cassandra 的一种方言语法,对应于文档数据库的存储模型;而 SQL API 是直接基于 PostgresQL 魔改的,能比较好地兼容 PG 语法,据官方说这样可以更方便地跟随 PG 新特性,有没有官方说的这么美好就不得而知了。

Yugabyte 的存储层才是重头戏。其中 TServer 负责存储 tablet,每个 tablet 对应一个 Raft Group,分布在三个不同的节点上,以此保证高可用性。Master 负责元数据管理,除了 tablet 的位置信息,还包括表结构等信息。Master 本身也依靠 Raft 实现高可用。

基于 Tablet 的分布式存储

这一部分是 HBase/Spanner 精髓部分,Cockroach/TiDB 的做法几乎也是一模一样的。每张表被分成很多个 tablet,tablet 是数据分布的最小单元,通过在节点间搬运 tablet 以及 tablet 的分裂与合并,就可以实现几乎无上限的 scale out。每个 tablet 有多个副本,形成一个 Raft Group,通过 Raft 协议保证数据的高可用和持久性,Group Leader 负责处理所有的写入负载,其他 Follower 作为备份。

和其他产品一样,Master 节点会负责协调 tablet 的搬运、分裂等操作,保证集群的负载均衡。这些操作是直接基于 Raft Group 实现的。

有趣的是,Yugabyte 的分区采用哈希分区(TiDB 和 Cockroach 都使用范围分区)。分区的方式是将 key 哈希映射到 2 字节的空间中(即 0x0000 到 0xFFFF),这个空间又被划分成多个范围,比如下图的例子中被划分为 16 个范围,每个范围的 key 落在一个 tablet 中。理论上说最多可能有 64K 个 tablet,这对实际使用足够了。哈希分区的好处是插入数据(尤其是从尾部 append 数据)时不会出现热点;坏处是对于小范围的范围扫描(例如 pk BETWEEN 1 AND 10)性能不占优势。


基于 RocksDB 的本地存储

每个 TServer 节点上的本地存储称为 DocDB。和 TiDB/Cockroach 一样,Yugabyte 也用 RocksDB 来做本地存储。这一层需要将关系型 tuple 以及文档编码为 key-value 保存到 RocksDB 中,下图是对文档数据的编码方式,其中有不少是为了兼容 Cassandra 设计的,可以忽略这些,主要关注以下几个部分:

key 中包含
1.16-bit hash:依靠这个值才能做到哈希分区
2.主键数据(对应图中 hash/range columns)
3.column ID:因为每个 tuple 有多个列,每个列在这里需要用一个 key-value 来表示
4.hybrid timestamp:用于 MVCC 的时间戳

value 中包含:column 的值

如果撇开文档模型,key-value 的设计很像 Cockroach:每个 cell (一行中的一列数据)对应一个 key-value。而 TiDB 是每个 tuple 打包成一个 key-value。个人比较偏好 TiDB 的做法。

分布式事务:2PC & MVCC

和 TiDB/Cockroach 一样,Yugabyte 也采用了 MVCC 结合 2PC 的事务实现。

时间戳

时间戳是分布式事务的关键选型之一。Yugabyte 和 Cockroach 一样选择的是 Hybrid Logical Clock (HLC)。

HLC 将时间戳分成物理(高位)和逻辑(低位)两部分,物理部分对应 UNIX 时间戳,逻辑部分对应 Lamport 时钟。在同一毫秒以内,物理时钟不变,而逻辑时钟就和 Lamport 时钟一样处理——每当发生信息交换(RPC)就需要更新时间戳,从而确保操作与操作之间能够形成一个偏序关系;当下一个毫秒到来时,逻辑时钟部分归零。

不难看出,HLC 的正确性其实是由 Logical Clock 来保证的:它相比 Logical Clock 只是在每个毫秒引入了一个额外的增量,显然这不会破坏 Logical Clock 的正确性。但是,物理部分的存在将原本无意义的时间戳赋予了物理意义,提高了实用性。HLC 是除了 TrueTime 以外最好的时间戳实现了,唯一的缺点是不能提供真正意义上的外部一致性,仅仅能保证相关事务之间的“外部一致性”。另一种方案是引入中心授时节点(TSO),也就是 TiDB 使用的方案。TSO 方案要求所有事务必须从 TSO 获取时间戳,实现相对简单,但引入了更多的网络 RPC,而且 TSO 过于关键——短时间的不可用也是很危险的。


事务提交

毫不惊奇,Yugabyte 的分布式事务同样是基于 2PC 的。其做法接近 Cockroach。事务提交过程中,它会在 DocDB 存储里面写入一些临时的记录(provisional records),包括以下三种类型:

1.Primary provisional records:还未提交完成的数据,多了一个事务ID,也扮演锁的角色;
2.Transaction metadata:事务状态所在的 tablet ID。因为事务状态表很特殊,不是按照 hash key 分片的,所以需要在这里记录一下它的位置;
3.Reverse Index:所有本事务中的 primary provisional records,便于恢复使用。


事务的状态信息保存在另一个 tablet 上,包括三种可能的状态:Pending、Committed 或 Aborted。事务从 Pending 状态开始,终结于 Committed 或 Aborted。

事务状态就是 Commit Point 的那个“开关”,当事务状态切换到 Commited 的一瞬间,就意味着事务的成功提交。这是保证整个事务原子性的关键。

完整的提交流程如下图所示:


另外,Yugabyte 文档中提到它除了 Snapshot Isolation 还支持 Serializable 隔离级别,但是似乎没有看到他是如何规避 Write Skew 问题的。从 Release Notes 看来这应该是 2.0 GA 中新增加的功能。

竞品对比

以下表格摘自 Compare YugabyteDB to other databases:



最新版本:2.11
2021年11月下旬发布的YugabyteDB 2.11带来了广泛使用的 PostgreSQL 特性,包括外部数据包装器(FDW)、GIN 索引、排序(Collation)支持、追随者读取、改进的空间放大和并发事务的 read-committed 隔离级别。

外部数据包装器允许 PostgreSQL 数据库将远程数据库中的表视为本地可用的表。这使开发人员能够毫不费力地编写查询,访问外部数据源中的数据,就好像它们是来自 PostgreSQL 数据库中的表一样。对 FDW 的支持允许开发人员编写一个访问外部云原生数据库中数据的 JOIN 查询。

Read Committed 事务隔离:PostgreSQL 使用一种叫做多版本并发控制的技术来隔离并发事务并确保数据一致性。SQL-92 标准定义了四个级别的事务隔离;可序列化、可重复读取、read committed和 read uncommitted。

YugabyteDB 此前已经支持两个最严格的隔离级别,可序列化和快照(类似于可重复读取)。YugabyteDB 2.11 增加了对 read committed 事务隔离的支持,这也是 PostgreSQL 的默认隔离级别。其他值得注意的内容:
GIN 索引:这些加速了表内多列的文本搜索
排序:这提供了对数据的排序顺序和字符分类行为的精细控制
支持分析:这允许你收集关于数据库表的内容的统计数据,从而实现查询的高效执行计划。
支持 Dapper 和 Mybatis ORMs:这些简化了 .Net 和 Java 应用程序的开发
支持 pg_stat_monitor:这是一个流行的 PostgreSQL 的查询性能监控工具
……

更多详情可查看此处


官方主页:
https://www.yugabyte.com/

https://github.com/yugabyte