暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

分布式数据库YugabyteDB

大数据真有意思 2020-11-28
722

点击关注上方“知了小巷”,

设为“置顶或星标”,第一时间送达干货。 

前言


https://www.yugabyte.com/
https://github.com/yugabyte/yugabyte-db
https://docs.yugabyte.com/
Spanner论文(中文版)点击文末阅读原文可以下载PDF

YugabyteDB是一个全球部署的分布式数据库,和国内的TiDB及国外的CockroachDB类似,也是受到Google Spanner论文启发,所以在很多地方这几个数据库存在不少相似之处。

CockroachDB类似,YugabyteDB也主打全球分布式的事务数据库——不仅能把节点部署到全球各地,还能完整支持ACID事务,这是他最大的卖点。除此以外还有一些独特的特性,比如支持文档数据库接口

系统架构


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

YugabyteDB的查询层支持SQL和CQL两种API,其中CQL是兼容Cassandra的一种方言语法,对应于文档数据库的存储模型;而SQL API是直接基于PostgresQL修改的,能比较好地兼容PG语法,据官方说这样可以更方便地持续兼容PG新特性。

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

基于tablet的分布式存储


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

下图是一个例子:一张表被分成16个tablet,tablet的副本和Raft Group leader均匀分布在各个节点上,分别保证了数据的均衡和负载的均衡。

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

在数据分区或分片上,YugabyteDB当前支持两种数据分片方式:一致性hash分片和范围分片。

For every given key, there is exactly one tablet that owns it.
YugabyteDB currently supports two ways of sharding data - hash (aka consistent hash) sharding and range sharding.

哈希方式是将key哈希映射到2个字节的空间中(即0x0000
0xFFFF
),这个空间又被划分成多个子范围,比如下图的例子中被划分为16个子范围([0x0000, 0x1000), [0x1000, 0x2000), … , [0xF000, 0xFFFF]
),每个子范围的key落在一个tablet中。理论上说最多可能有64K个tablet,这对实际使用足够了。

哈希的好处是插入数据(尤其是从尾部append数据)时不会出现热点;坏处是对于小范围的范围扫描(例如pk BETWEEN 1 AND 10)性能会比较吃亏。

Hash-sharded tables

hash分片创建表示例:

-- hash的字段:customer_id
CREATE TABLE customers (
    customer_id bpchar NOT NULL,
    company_name character varying(40NOT NULL,
    contact_name character varying(30),
    contact_title character varying(30),
    address character varying(60),
    city character varying(15),
    region character varying(15),
    postal_code character varying(10),
    country character varying(15),
    phone character varying(24),
    fax character varying(24),
    PRIMARY KEY (customer_id HASH)
SPLIT INTO 16 TABLETS;

Range-sharded tables

-- 范围分区的字段:customer_id ASC
CREATE TABLE customers (
    customer_id bpchar NOT NULL,
    company_name character varying(40NOT NULL,
    contact_name character varying(30),
    contact_title character varying(30),
    address character varying(60),
    city character varying(15),
    region character varying(15),
    postal_code character varying(10),
    country character varying(15),
    phone character varying(24),
    fax character varying(24),
    PRIMARY KEY (customer_id ASC)
SPLIT AT ((1000), (2000), (3000), ... );

基于 RocksDB 的本地存储


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

key中包含:

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

value中包含: column的值

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

分布式事务:2PC & MVCC


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

时间戳


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

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

HLC的正确性其实是由Logical Clock来保证的:在每个毫秒引入了一个额外的增量,不会破坏Logical Clock的正确性。但是,物理部分的存在将原本无意义的时间戳赋予了物理意义,提高了实用性。

另一种方案是引入中心授时节点(TSO),也就是TiDB使用的方案。TSO方案要求所有事务必须从TSO获取时间戳,实现相对简单,但引入了更多的网络RPC,而且 TSO过于关键——短时间的不可用也是极为危险的。

HLC的实现中有一些很tricky的地方,比如文档中提到的Safe timestamp assignment for a read request。对于同一事务中的多次read,问题还要更复杂,有兴趣的读者可以看CockroachDB团队的这篇博客Living Without Atomic Clocks。

https://docs.yugabyte.com/latest/architecture/transactions/single-row-transactions/#safe-timestamp-assignment-for-a-read-request
https://www.cockroachlabs.com/blog/living-without-atomic-clocks/
Cloud Spanner:TrueTime 和外部一致性

https://cloud.google.com/spanner/docs/true-time-external-consistency

事务提交


YugabyteDB的分布式事务同样是基于2PC的。做法接近CockroachDB。事务提交过程中,会在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的一瞬间,就意味着事务的成功提交。这是保证整个事务原子性的关键。

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

竞品对比


https://docs.yugabyte.com/latest/comparisons/


- END -


文章转载自大数据真有意思,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论