怎样打造一个分布式数据库

在技术方面,我本身热衷于 Open Source,写了不少 Open Source 的东西,擅长的是 Infrastructure 领域。Infrastructure 领域如今范围很广,好比说很典型的分布式 Scheduler、Mesos、Kubernetes,另外它和 Microservices 所结合的东西也特别多。Infrastructure 领域还有好比 Database 有分 AP(分析型)和 TP(事务型),好比说很典型的你们知道的 Spark、Greenplum、Apache Phoenix 等等,这些都属于在 AP 的,它们也会去尝试支持有限的 TP。另外,还有一个比较有意思的就是 Kudu——Cloudera Open Source 的那个项目,它的目标颇有意思:我不作最强的 AP 系统,也不作最强的 TP 系统,我选择一个相对折中的方案。从文化哲学上看,它比较符合中国的中庸思想。程序员

另外,我前后建立了 Codis、TiDB。去年12月份建立了 TiKV 这个 project,TiKV 在全部的 rust 项目里目前排名前三。算法

首先咱们聊聊 Database 的历史,在已经有这么多种数据库的背景下咱们为何要建立另一个数据库;以及说一下如今方案遇到的困境,说一下 Google Spanner 和 F一、TiKV 和 TiDB,说一下架构的事情,在这里咱们会重点聊一下 TiKV。由于咱们产品的不少特性是 TiKV 提供的,好比说跨数据中心的复制、Transaction、auto-scale。sql

 

接下来聊一下为何 TiKV 用 Raft 能实现全部这些重要的特性,以及 scale、MVCC 和事务模型。东西很是多,我今天不太可能把里面的技术细节都描述得特别细,由于几乎每个话题均可以找到一篇或者是多篇论文,因此详细的技术问题你们能够单独来找我聊。微信

后面再说一下咱们如今遇到的窘境,就是你们常规遇到的分布式方案有哪些问题,好比 MySQL Sharding。咱们建立了无数 MySQL Proxy,好比官方的 MySQL proxy、Youtube 的 Vitess、淘宝的 Cobar、TDDL以及基于 Cobar 的 MyCAT、金山的 Kingshard、360 的 Atlas、京东的 JProxy,我在豌豆荚也写了一个。能够说,随便一个大公司都会造一个 MySQL Sharding 的方案。网络

为何咱们要建立另一个数据库?

昨天晚上我还跟一个同窗聊到,基于 MySQL 的方案它的天花板在哪里,它的天花板特别明显。有一个思路是能不能经过 MySQL 的 server 把 InnoDB 变成一个分布式数据库,听起来这个方案很完美,可是很快就会遇到天花板。由于 MySQL 生成的执行计划是个单机的,它认为整个计划的 cost 也是单机的,我读取一行和读取下一行之间的开销是很小的,好比迭代 next row 能够马上拿到下一行。实际上在一个分布式系统里面,这是不必定的。架构

 

另外,你把数据都拿回来计算这个太慢了,不少时候咱们须要把咱们的 expression 或者计算过程等等运算推下去,向上返回一个最终的计算结果,这个必定要用分布式的 plan,前面控制执行计划的节点,它必需要理解下面是分布式的东西,才能生成最好的 plan,这样才能实现最高的执行效率。app

好比说你作一个 sum,你是一条条拿回来加,仍是让一堆机器一块儿算,最后给我一个结果。 例如我有 100 亿条数据分布在 10 台机器上,并行在这 10台机器我可能只拿到 10 个结果,若是把全部的数据每一条都拿回来,这就太慢了,彻底丧失了分布式的价值。聊到 MySQL 想实现分布式,另一个实现分布式的方案就是 Proxy。可是 Proxy 自己的天花板在那里,就是它不支持分布式的 transaction,它不支持跨节点的 join,它没法理解复杂的 plan,一个复杂的 plan 打到 Proxy 上面,Proxy 就傻了,我到底应该往哪个节点上转发呢,若是我涉及到 subquery sql 怎么办?因此这个天花板是瞬间会到,在传统模型下面的修改,很快会达不到咱们的要求。

另一个很重要的是,MySQL 支持的复制方式是半同步或者是异步,可是半同步能够降级成异步,也就是说任什么时候候数据出了问题你不敢切换,由于有多是异步复制,有一部分数据尚未同步过来,这时候切换数据就不一致了。前一阵子出现过某公司忽然不能支付了这种事件,今年有不少这种相似的 case,因此微博上你们都在说“说好的异地多活呢?”……

为何传统的方案在这上面解决起来特别的困难,天花板立刻到了,基本上不可能解决这个问题。另外是多数据中心的复制和数据中心的容灾,MySQL 在这上面是作很差的。

在前面三十年基本上是关系数据库的时代,那个时代建立了不少伟大的公司,好比说 IBM、Oracle、微软也有本身的数据库,早期还有一个公司叫 Sybase,有一部分特别老的程序员同窗在当年的教程里面还能够找到这些东西,可是如今基本上看不到了。

另外是 NoSQL。NoSQL 也是一度很是火,像 Cassandra、MongoDB 等等,这些都属于在互联网快速发展的时候建立这些可以 scale 的方案,但 Redis scale 出来比较晚,因此不少时候你们把 Redis 当成一个 Cache,如今慢慢你们把它当成存储不那么重要的数据的数据库。由于它有了 scale 支持之后,你们会把更多的数据放在里面。

而后到了 2015,严格来说是到 2014 年到 2015 年之间,Raft 论文发表之后,真正的 NewSQL 的理论基础终于完成了。我以为 NewSQL 这个理论基础,最重要的划时代的几篇论文,一个是谷歌的 Spanner,是在 2013 年初发布的;再就是 Raft 是在 2014 年上半年发布的。这几篇至关于打下了分布式数据库 NewSQL 的理论基础,这个模型是很是重要的,若是没有模型在上面是堆不起来东西的。说到如今,你们可能对于模型仍是能够理解的,可是对于它的实现难度很难想象。

前面我大概提到了咱们为何须要另一个数据库,说到 Scalability 数据的伸缩,而后咱们讲到须要 SQL,好比你给我一个纯粹的 key-velue 系统的 API,好比我要查找年龄在 10 岁到 20 岁之间的 email 要知足一个什么要求的。若是只有 KV 的 API 这是会写死人的,要写不少代码,可是实际上用 SQL 写一句话就能够了,并且 SQL 的优化器对整个数据的分布是知道的,它能够很快理解你这个 SQL,而后会获得一个最优的 plan,他获得这个最优的 plan 基本上等价于一个真正理解 KV 每一步操做的人写出来的程序。一般状况下,SQL 的优化器是为了更加了解或者作出更好的选择。

另一个就是 ACID 的事务,这是传统数据库必需要提供的基础。之前你不提供 ACID 就不能叫数据库,可是近些年你们写一个内存的 map 也能够叫本身是数据库。你们写一个 append-only 文件,咱们也能够叫只读数据库,数据库的概念比之前极大的泛化了。

另外就是高可用和自动恢复,他们的概念是什么呢?有些人会有一些误解,由于今天还有朋友在现场问到,出了故障,好比说一个机房挂掉之后我应该怎么作切换,怎么操做。这个实际上至关于仍是上一代的概念,还须要人去干预,这种不算是高可用。

将来的高可用必定是系统出了问题立刻能够自动恢复,立刻能够变成可用。好比说一个机房挂掉了,十秒钟不能支付,十秒钟以后系统自动恢复了变得能够支付,即便这个数据中心不再起来我整个系统仍然是能够支付的。Auto-Failover 的重要性就在这里。你们不但愿在睡觉的时候被一个报警给拉起来,我相信你们之后具有这样一个能力,5 分钟之内的报警不用理会,挂掉一个机房,又挂掉一个机房,这种连续报警才会理。咱们内部开玩笑说,但愿你们都能睡个好觉,很重要的事情就是这个。

说完应用层的事情,如今颇有不少业务,在应用层本身去分片,好比说我按照 user ID在代码里面分片,还有一部分是更高级一点我会用到一致性哈希。问题在于它的复杂度,到必定程度以后我自动的分库,自动的分表,我以为下一代数据库是不须要理解这些东西的,不须要了解什么叫作分库,不须要了解什么叫作分表,由于系统是所有自动搞定的。同时复杂度,若是一个应用不支持事务,那么在应用层去作,一般的作法是引入一个外部队列,引入大量的程序机制和状态转换,A 状态的时候容许转换到 B 状态,B 状态容许转换到 C 状态。

举一个简单的例子,好比说在京东上买东西,先下订单,支付状态以后这个商品才能出库,若是不是支付状态必定不能出库,每一步都有严格的流程。

Google Spanner / F1

说一下 Google 的 Spanner 和 F1,这是我很是喜欢的论文,也是我最近几年看过不少遍的论文。 Google Spanner 已经强大到什么程度呢?Google Spanner 是全球分布的数据库,在国内目前广泛作法叫作同城两地三中心,它们的差异是什么呢?以 Google 的数据来说,谷歌比较高的级别是他们有 7 个副本,一般是美国保存 3 个副本,再在另外 2 个国家能够保存 2 个副本,这样的好处是万一美国两个数据中心出了问题,那整个系统还能继续可用,这个概念就是好比美国 3 个副本全挂了,整个数据都还在,这个数据安全级别比不少国家的安全级别还要高,这是 Google 目前作到的,这是全球分布的好处。

如今国内主流的作法是两地三中心,但如今基本上都不能自动切换。你们能够看到不少号称实现了两地三中心或者异地多活,可是一出现问题都说很差意思这段时间我不能提供服务了。你们无数次的见到这种 case, 我就不列举了。

Spanner 如今也提供一部分 SQL 特性。在之前,大部分 SQL 特性是在 F1 里面提供的,如今 Spanner 也在逐步丰富它的功能,Google 是全球第一个作到这个规模或者是作到这个级别的数据库。事务支持里面 Google 有点黑科技(其实也没有那么黑),就是它有GPS 时钟和原子钟。你们知道在分布式系统里面,好比说数千台机器,两个事务启动前后顺序,这个顺序怎么界定(事务外部一致性)。这个时候 Google 内部使用了 GPS 时钟和原子钟,正常状况下它会使用一个GPS 时钟的一个集群,就是说我拿的一个时间戳,并非从一个 GPS 上来拿的时间戳,由于你们知道全部的硬件都会有偏差。若是这时候我从一个上拿到的 GPS 自己有点问题,那么你拿到的这个时钟是不精确的。而 Google 它其实是在一批 GPS 时钟上去拿了可以知足 majority 的精度,再用时间的算法,获得一个比较精确的时间。你们知道 GPS 也不太安全,由于它是美国军方的,对于 Google 来说要实现比国家安全级别更高的数据库,而 GPS 是可能受到干扰的,由于 GPS 信号是能够调整的,这在军事用途上面很典型的,你们知道导弹的制导须要依赖 GPS,若是调整了 GPS 精度,那么导弹精度就废了。因此他们还用原子钟去校订 GPS,若是 GPS 忽然跳跃了,原子钟上是能够检测到 GPS 跳跃的,这部分相对有一点黑科技,可是从原理上来说仍是比较简单,比较好理解的。

最开始它 Spanner 最大的用户就是 Google 的 Adwords,这是 Google 最赚钱的业务,Google 就是靠广告生存的,咱们一直以为 Google 是科技公司,可是他的钱是从广告那来的,因此必定程度来说 Google 是一个广告公司。Google 内部的方向先有了 Big table ,而后有了 MegaStore ,MegaStore 的下一代是 Spanner ,F1 是在 Spanner 上面构建的。

TiDB and TiKV

TiKV 和 TiDB 基本上对应 Google Spanner 和 Google F1,用 Open Source 方式重建。目前这两个项目都开放在 GitHub 上面,两个项目都比较火爆,TiDB 是更早一点开源的, 目前 TiDB 在 GitHub 上 有 4300 多个 Star,天天都在增加。

另外,对于如今的社会来说,咱们以为 Infrastructure 领域闭源的东西是没有任何生存机会的。没有任何一家公司,愿意把本身的身家性命压在一个闭源的项目上。举一个很典型的例子,在美国有一个数据库叫 FoundationDB,去年被苹果收购了。FoundationDB 以前和用户签的合约都是一年的合约。好比说,我给你服务周期是一年,如今我被另一个公司收购了,我今年服务到期以后,我是知足合约的。可是其余公司不再能找它服务了,由于它如今不叫 FoundationDB 了,它叫 Apple了,你不能找 Apple 给你提供一个 Enterprise service。

TiDB 和 TiKV 为何是两个项目,由于它和 Google 的内部架构对比差很少是这样的:TiKV 对应的是 Spanner,TiDB 对应的是 F1 。F1 里面更强调上层的分布式的 SQL 层到底怎么作,分布式的 Plan 应该怎么作,分布式的 Plan 应该怎么去作优化。同时 TiDB 有一点作的比较好的是,它兼容了 MySQL 协议,当你出现了一个新型的数据库的时候,用户使用它是有成本的。你们都知道做为开发很讨厌的一个事情就是,我要每一个语言都写一个 Driver,好比说你要支持 C++、你要支持 Java、你要支持 Go 等等,这个太累了,并且用户还得改他的程序,因此咱们选择了一个更加好的东西兼容 MySQL 协议,让用户能够不用改。一会我会用一个视频来演示一下,为何一行代码不改就能够用,用户就能体会到 TiDB 带来的全部的好处。

这个图其实是整个协议栈或者是整个软件栈的实现。你们能够看到整个系统是高度分层的,从最底下开始是 RocksDB ,而后再上面用 Raft 构建一层能够被复制的 RocksDB ,在这一层的时候它尚未 Transaction,可是整个系统如今的状态是全部写入的数据必定要保证它复制到了足够多的副本。也就是说只要我写进来的数据必定有足够多的副本去 cover 它,这样才比较安全,在一个比较安全的 Key-value store 上面, 再去构建它的多版本,再去构建它的分布式事务,而后在分布式事务构建完成以后,就能够轻松的加上 SQL 层,再轻松的加上MySQL 协议的支持。而后,这两天我比较好奇,本身写了 MongoDB 协议的支持,而后咱们能够用 MongoDB 的客户端来玩,就是说协议这一层是高度可插拔的。TiDB 上能够在上面构建一个 MongoDB 的协议,至关于这个是构建一个 SQL 的协议,能够构建一个 NoSQL 的协议。这一点主要是用来验证 TiKV 在模型上面的支持能力。

这是整个 TiKV 的架构图,从这个看来,整个集群里面有不少 Node,好比这里画了四个 Node ,分别对应了四个机器。每个 Node 上能够有多个 Store,每一个 Store 里面又会有不少小的 Region,就是说一小片数据,就是一个 Region 。从全局来看全部的数据被划分红不少小片,每一个小片默认配置是 64M,它已经足够小,能够很轻松的从一个节点移到另一个节点,Region 1 有三个副本,它分别在 Node一、Node 2 和 Node4 上面, 相似的Region 2,Region 3 也是有三个副本。每一个 Region 的全部副本组成一个 Raft Group,整个系统能够看到不少这样的 Raft groups。

Raft 细节我不展开了,你们有兴趣能够找我私聊或者看一下相应的资料。

由于整个系统里面咱们能够看到上一张图里面有不少 Raft group 给咱们,不一样 Raft group 之间的通信都是有开销的。因此咱们有一个相似于 MySQL 的 group commit 机制 ,你发消息的时候实际上能够 share 同一个 connection , 而后 pipeline + batch 发送,很大程度上能够省掉大量 syscall 的开销。

另外,其实在必定程度上后面咱们在支持压缩的时候,也有很是大的帮助,就是能够减小数据的传输。对于整个系统而言,可能有数百万的 Region,它的大小能够调整,好比说 64M、128M、256M,这个实际上依赖于整个系统里面当前的情况。

好比说咱们曾经在有一个用户的机房里面作过测试,这个测试有一个香港机房和新加坡的机房。结果咱们在作复制的时候,新加坡的机房大于 256M 就复制不过去,由于机房很不稳定,必需要保证数据切的足够小,这样才能复制过去。

若是一个 Region 太大之后咱们会自动作 SPLIT,这是很是好玩的过程,有点像细胞的分裂。

而后 TiKV 的 Raft 实现,是从 etcd 里面 port 过来的,为何要从 etcd 里面 port 过来呢?首先 TiKV 的 Raft 实现是用 Rust 写的。做为第一个作到生产级别的 Raft 实现,因此咱们从 etcd 里面把它用 Go 语言写的 port 到这边。

这个是 Raft 官网上面列出来的 TiKV在里面的状态,你们能够看到 TiKV 把全部 Raft 的 feature 都实现了。 好比说 Leader Election、Membership Changes,这个是很是重要的,整个系统的 scale 过程高度依赖 Membership Changes,后面我用一个图来说这个过程。后面这个是 Log Compaction,这个用户不太关心。

这是很典型的细胞分裂的图,实际上 Region 的分裂过程和这个是相似的。

咱们看一下扩容是怎么作的。

好比说以如今的系统假设,咱们刚开始说只有三个节点,有 Region1 分别是在 1 、二、4,我用虚线链接起来表明它是一个 Raft group ,你们能够看到整个系统里面有三个 Raft group ,在每个 Node 上面数据的分布是比较均匀的,在这个假设每个 Region 是 64M ,至关于只有一个 Node 上面负载比其余的稍微大一点点。

一个在线视频默认咱们都是推荐 3 个副本或者 5 个副本的配置。Raft 自己有一个特色,若是一个 leader down 掉以后,其它的节点会选一个新的 leader ,那么这个新的 leader 会把它尚未 commit 但已经 reply 过去的 log 作一个 commit ,而后会再作 apply ,这个有点偏 Raft 协议,细节我不讲了。

复制数据的小的 Region,它其实是跨多个数据中心作的复制。这里面最重要的一点是永远不丢失数据,不管如何我保证个人复制必定是复制到 majority ,任什么时候候我只要对外提供服务,容许外面写入数据必定要复制到 majority 。很重要的一点就是恢复的过程必定要是自动化的,我前面已经强调过,若是不能自动化恢复,那么中间的宕机时间或者对外不可服务的时间,便不是由整个系统决定的,这是相对回到了几十年前的状态。

MVCC

MVCC 我稍微仔细讲一下这一块。MVCC 的好处,它很好支持 Lock-free 的 snapshot read ,一下子我有一个图会展现 MVCC 是怎么作的。isolation level 就不讲了, MySQL 里面的级别是能够调的,咱们的 TiKV 有 SI,还有 SI+lock,默认是支持 SI 的这种隔离级别,而后你写一个 select for update 语句,这个会自动的调整到 SI 加上 lock 这个隔离级别。这个隔离级别基本上和 SSI 是一致的。还有一个就是 GC 的问题,若是你的系统里面的数据产生了不少版本,你须要把这个比较老的数据给 GC 掉,好比说正常状况下咱们是不删除数据的, 你写入一行,而后再写入一行,不断去 update 同一行的时候,每一次 update 会产生新的版本,新的版本就会在系统里存在,因此咱们须要一个 GC 的模块把比较老的数据给 GC 掉,实际上这个 GC 不是 Go 里面的GC,不是 Java 的 GC,而是数据的 GC。

这是一个数据版本,你们能够看到咱们的数据分红两块,一个是 meta,一个是 data。meta 相对于描述个人数据当前有多少个版本。你们能够看到绿色的部分,好比说咱们的 meta key 是 A ,keyA 有三个版本,是 A1 、A二、A3,咱们把 key 本身和 version 拼到一块儿。那咱们用 A一、A二、A3 分别描述 A 的三个版本,那么就是 version 1/2/3。meta 里面描述,就是个人整个 key 相对应哪一个版本,我想找到那个版本。好比说我如今要读取 key A 的版本10,但显然如今版本 10 是没有的,那么小于版本 10 最大的版本是 3,因此这时我就能读取到 3,这是它的隔离级别决定的。关于 data,我刚才已经讲过了。

分布式事务模型

接下来是分布式事务模型,实际上是基于 Google Percolator,这是 Google 在 2006 发表的一篇论文,是 Google 在作内部增量处理的时候发现了这个方法,本质上仍是二阶段提交的。这使用的是一个乐观锁,好比说我提供一个 transaction ,我去改一个东西,改的时候是发布在本地的,并无立刻 commit 到数据存储那一端,这个模型就是说,我修改的东西我立刻去 Lock 住,这个基本就是一个悲观锁。但若是到最后一刻我才提交出去,那么锁住的这一小段的时间,这个时候实现的是乐观锁。乐观锁的好处就是当你冲突很小的时候能够获得很是好的性能,由于冲突特别小,因此我本地修改一般都是有效的,因此我不须要去 Lock ,不须要去 roll back 。本质上分布式事务就是 2PC (两阶段提交) 或者是 2+x PC,基本上没有 1PC,除非你在别人的级别上作弱化。好比说我容许你读到当前最新的版本,也容许你读到前面的版本,书里面把这个叫作幻读。若是你调到这个程度是比较容易作 1PC 的,这个实际上仍是依赖用户设定的隔离级别的,若是用户须要更高的隔离级别,这个 1PC就不太好作了。

这是一个路由,正常来说,你们可能会好奇一个 SQL 语句怎么最后会落到存储层,而后能很好的运行,最后怎么能映射到 KV 上面,又怎么能路由到正确的节点,由于整个系统可能有上千个节点,你怎么能正确路由到那一个的节点。咱们在 TiDB 有一个 TiKV driver , 另外 TiKV 对外使用的是 Google Protocol Buffer 来做为通信的编码格式。

Placement Driver

来讲一下 Placement Driver 。Placement Driver 是什么呢?整个系统里面有一个节点,它会时刻知道如今整个系统的状态。好比说每一个机器的负载,每一个机器的容量,是否有新加的机器,新加机器的容量究竟是怎么样的,是否是能够把一部分数据挪过去,是否是也是同样下线, 若是一个节点在十分钟以内没法被其余节点探测到,我认为它已经挂了,无论它其实是不是真的挂了,可是我也认为它挂了。由于这个时候是有风险的,若是这个机器万一真的挂了,意味着你如今机器的副本数只有两个,有一部分数据的副本数只有两个。那么如今你必须立刻要在系统里面从新选一台机器出来,它上面有足够的空间,让我如今只有两个副本的数据从新再作一份新的复制,系统始终维持在三个副本。整个系统里面若是机器挂掉了,副本数少了,这个时候应该会被自动发现,立刻补充新的副本,这样会维持整个系统的副本数。这是很重要的 ,为了不数据丢失,必须维持足够的副本数,由于副本数每少一个,你的风险就会再增长。这就是 Placement Driver 作的事情。

同时,Placement Driver 还会根据性能负载,不断去 move 这个 data 。好比说你这边负载已经很高了,一个磁盘假设有 100G,如今已经用了 80G,另一个机器上也是 100G,可是他只用了 20G,因此这上面还能够有几十 G 的数据,好比 40G 的数据,你能够 move 过去,这样能够保证系统有很好的负载,不会出现一个磁盘巨忙无比,数据已经多的装不下了,另一个上面尚未东西,这是 Placement Driver 要作的东西。

Raft 协议还提供一个很高级的特性叫 leader transfer。leader transfer 就是说在我不移动数据的时候,我把个人 leadership 给你,至关于从这个角度来说,我把流量分给你,由于我是 leader,因此数据会到我这来,但我如今把 leader给你,我让你来当 leader,原来打给个人请求会被打给你,这样个人负载就降下来。这就能够很好的动态调整整个系统的负载,同时又不搬移数据。不搬移数据的好处就是,不会造成一个抖动。

MySQL Sharding

MySQL Sharding 我前面已经提到了它的各类天花板,MySQL Sharding 的方案很典型的就是解决基本问题之后,业务稍微复杂一点,你在 sharding 这一层根本搞不定。它永远须要一个 sharding key,你必需要告诉个人 proxy,个人数据要到哪里找,对用户来讲是极不友好的,好比我如今是一个单机的,如今我要切入到一个分布式的环境,这时我必需要改个人代码,我必需要知道我这个 key ,个人 row 应该往哪里 Sharding。若是是用 ORM ,这个基本上就无法作这个事情了。有不少 ORM 它自己假设我后面只有一个 MySQL。但 TiDB 就能够很好的支持,由于我全部的角色都是对的,我不须要关注 Sharding 、分库、分表这类的事情。

这里面有一个很重要的问题没有提,我怎么作 DDL。若是这个表很是大的话,好比说咱们有一百亿吧,横跨了四台机器,这个时候你要给它作一个新的 Index,就是我要添加一个新的索引,这个时候你必需要不影响任何现有的业务,实际上这是多阶段提交的算法,这个是 Google 和 F1 一块儿发出来那篇论文。

简单来说是这样的,先把状态标记成 delete only ,delete only 是什么意思呢?由于在分布式系统里面,全部的系统对于 schema 的视野不是一致的,好比说我如今改了一个值,有一部分人发现这个值被改了,可是还有一部分人尚未开始访问这个,因此根本不知道它被改了。而后在一个分布系统里,你也不可能实时通知到全部人在同一时刻发现它改变了。好比说从有索引到没有索引,你不能一步切过去,由于有的人认为它有索引,因此他给它建了一个索引,可是另一个机器他认为它没有索引,因此他就把数据给删了,索引就留在里面了。这样遇到一个问题,我经过索引找的时候告诉我有, 实际数据却没有了,这个时候一致性出了问题。好比说我 count 一个 email 等于多少的,我经过 email 建了一个索引,我认为它是在,可是 UID 再转过去的时候可能已经不存在了。

好比说我先标记成 delete only,我删除它的时候无论它如今有没有索引,我都会尝试删除索引,因此个人数据是干净的。若是我删除掉的话,我无论结果是什么样的,我尝试去删一下,可能这个索引还没 build 出来,可是我仍然删除,若是数据没有了,索引必定没有了,因此这能够很好的保持它的一致性。后面再相似于前面,先标记成 write only 这种方式,连续再迭代这个状态,就能够迭代到一个最终能够对外公开的状态。好比说当我迭代到必定程度的时候,我能够从后台 build index ,好比说我一百亿,正在操做的 index 会立刻 build,可是还有不少没有 build index ,这个时候后台不断的跑 map-reduce 去 build index ,直到整个都 build 完成以后,再对外 public ,就是说我这个索引已经可用了,你能够直接拿索引来找,这个是很是经典的。在这个 Online,Asynchronous Schema Change in F1 paper以前,你们都不知道这事该怎么作。

Proxy Sharding 的方案不支持分布式事务,更不用说跨数据中心的一致性事务了。 TiKV 很好的支持 transaction,刚才提到的 Raft 除了增长副本以外,还有 leader transfer,这是一个传统的方案都没法提供的特性。以及它带来的好处,当我瞬间平衡整个系统负载的时候,对外是透明的, 作 leader transfer 的时候并不须要移动数据,只是个简单的 leader transfer 消息。

而后说一下若是你们想参与咱们项目的话是怎样的过程,由于整个系统是彻底开源的,若是你们想参与其中任何一部分均可以,好比说我想参与到分布式 KV,能够直接贡献到 TiKV。TiKV 须要写 Rust,若是你们对这块特别有激情能够体验写 Rust 的感受 。

TiDB 是用 Go 写的,Go 在中国的群众基础是很是多的,目前也有不少人在贡献。整个 TiDB 和TiKV 是高度协做的项目,由于 TiDB 目前还用到了 etcd ,咱们在和 CoreOS 在密切的合做,也特别感谢 CoreOS 帮咱们作了不少的支持,咱们也为 CoreOS 的 etcd 提了一些 patch。同时,TiKV 使用 RocksDB ,因此咱们也为 RocksDB 提了一些 patch 和 test,咱们也很是感谢 Facebook RocksDB team 对咱们项目的支持。

另一个是 PD,就是咱们前面提的 Placement Driver,它负责监控整个系统。这部分的算法比较好玩,你们若是有兴趣的话,能够去本身控制整个集群的调度,它和 Kubernetes 或者是Mesos 的调度算法是不同的,由于它调度的维度实际上比那个要更多。好比说磁盘的容量,你的 leader 的数量,你的网络当前的使用状况,你的 IO 的负载和 CPU 的负载均可以放进去。同时你还可让它调度不要跨一个机房里面建多个副本。


感谢魏星对本文的审校。

给InfoQ中文站投稿或者参与内容翻译工做,请邮件至editors@cn.infoq.com。也欢迎你们经过新浪微博(@InfoQ@丁晓昀),微信(微信号:InfoQChina)关注咱们。

原文:http://www.infoq.com/cn/articles/how-to-build-a-distributed-database?utm_campaign=infoq_content&utm_source=infoq&utm_medium=feed&utm_term=global

相关文章
相关标签/搜索