7

分布式数据库YugabyteDB

 3 years ago
source link: https://mp.weixin.qq.com/s?__biz=MzA4NzA5NzE5Ng%3D%3D&%3Bmid=2650230545&%3Bidx=1&%3Bsn=749081549a75e367e4cf9d6d26087e0a
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

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

BbaQVru.png!mobile

前言

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实现高可用。

fuiiQ32.png!mobile

基于tablet的分布式存储

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

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

r2uuyub.png!mobile

和其他产品一样,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,这对实际使用足够了。

zmuIV3Q.png!mobile

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

Hash-sharded tables

v22m2m2.png!mobile

hash分片创建表示例:

-- hash的字段:customer_id
CREATE TABLE customers (
    customer_id bpchar NOT NULL,
    company_name character varying(40) NOT 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

MnuUJff.png!mobile

-- 范围分区的字段:customer_id ASC
CREATE TABLE customers (
    customer_id bpchar NOT NULL,
    company_name character varying(40) NOT 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的值

ui2iqiQ.png!mobile

如果撇开文档模型,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,便于恢复使用

B7nUvaJ.png!mobile

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

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

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

ZreAjqI.png!mobile

竞品对比

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

yEvuEb.png!mobile

- END -

nIzeUzu.png!mobile


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK