[CDAS 2016] 分布式数据库模式与反模式

文章分享qiuyesuifeng 发表了文章 • 0 个评论 • 413 次浏览 • 2016-10-11 15:12 • 来自相关话题

TiDB 是一个 OLTP 的数据库,我们主要 focus 在大数据的关系型数据库的存储和可扩展性,还有关系的模型,以及在线交易型数据库上的应用。所以,今天整个数据库的模式和反模式,我都会围绕着如何在一个海量的并发,海量的数据存... 查看全部


TiDB 是一个 OLTP 的数据库,我们主要 focus 在大数据的关系型数据库的存储和可扩展性,还有关系的模型,以及在线交易型数据库上的应用。所以,今天整个数据库的模式和反模式,我都会围绕着如何在一个海量的并发,海量的数据存储的容量上,去做在线实时的数据库业务的一些模式来讲。并从数据库的开发者角度,来为大家分享怎样写出更加适合数据库的一些程序。



基础软件的发展趋势


先简单介绍一下,现在我认为的一些基础软件上的发展趋势。


开源


第一点,开源是一个非常大的趋势。大家可以看到一些比较著名的基础软件,基本都是开源的,比如 Docker,比如 k8s。甚至在互联网公司里面用的非常多的软件,像 MySQL、Hadoop 等这种新一代的大数据处理的数据库等基础软件,也大多是开源的。其实这背后的逻辑非常简单:在未来其实你很难去将你所有的技术软件都用闭源, 因为开源会慢慢组成一个生态,而并不是被某一个公司绑定住。比如国家经常说去 IOE,为什么?很大的原因就是基本上你的业务是被基础软件绑死的,这个其实是不太好的一个事情。而且现在跟过去二十年前不一样,无论是开源软件的质量,还是社区的迭代速度,都已经是今非昔比,所以基本上开源再也不是低质低量的代名词,在互联网公司已经被验证很多次了。


分布式


第二,分布式会渐渐成为主流的趋势。这是为什么?这个其实也很好理解,因为随着数据量越来越大,大家可以看到,随着现在的硬件发展,我感觉摩尔定律有渐渐失效的趋势。所以单个节点的计算资源或者计算能力,它的增长速度是远比数据的增长速度要慢的。在这种情况下,你要完成业务,存储数据,要应对这么大的并发,只有一种办法就是横向的扩展。横向的扩展,分布式基本是唯一的出路。scale-up 和 scale-out 这两个选择其实我是坚定的站在 scale-out 这边。当然传统的关系数据库都会说我现在用的 Oracle,IBM DB2,他们现在还是在走 scale-up 的路线,但是未来我觉得 scale-out 的方向会渐渐成为主流。


碎片化


第三,就是整个基础软件碎片化。现在看上去会越来越严重。但是回想在十年前、二十年前,大家在写程序的时候,我上面一层业务,下面一层数据库。但是现在你会发现,随着可以给你选择的东西越来越多,可以给你在开源社区里面能用到的组件越来越多,业务越来越复杂,你会发现,像缓存有一个单独的软件,比如 redis,队列又有很多可以选择的,比如说 zeromq,rabbitmq,celery 各种各样的队列;数据库有 NoSQL、HBase,关系型数据库有 MySQL、PG 等各种各样的基础软件都可以选。但是就没有一个非常好东西能够完全解决自己的问题。所以这是一个碎片化的现状。


微服务


第四,是微服务的模式兴起。其实这个也是最近两年在软件架构领域非常火的一个概念。这个概念的背后思想,其实也是跟当年的 SOA 是一脉相承的。就是说一个大的软件项目,其实是非常难去 handle 复杂度的,当你业务变得越来越大以后,维护成本和开发成本会随着项目的代码量呈指数级别上升的。所以现在比较流行的就是,把各个业务之间拆的非常细,然后互相之间尽量做到无状态,整个系统的复杂度可以控制,是由很多比较简单的小的组件组合在一起,来对外提供服务的。
这个服务看上去非常美妙,一会儿会说有什么问题。最典型的问题就是,当你的上层业务都拆成无状态的小服务以后,你会发现原有的逻辑需要有状态的存储服务的时候你是没法拆的。我所有的业务都分成一小块,每一小块都是自己的数据库或者数据存储。比如说一个简单的 case,我每一个小部分都需要依赖同一个用户信息服务,这个信息服务会变成整个系统的一个状态集中的点,如果这个点没有办法做弹性扩展或者容量扩展的话,就会变成整个系统很致命的单点。


所以现在整个基础软件的现状,特别在互联网行业是非常典型的几个大的趋势。我觉得大概传统行业跟互联网行业整合,应该在三到五年,这么一个时间。所以互联网行业遇到的今天,可能就是传统行业,或者其他的行业会遇到的明天。所以,通过现在整个互联网里面,在数据存储、数据架构方面的一些比较新的思想,我们就能知道如何去做这个程序的设计,应对明天数据的量级。


现有存储系统的痛点


其实今天主要的内容是讲存储系统,存储系统现在有哪些痛点?其实我觉得在座的各位应该也都能切身的体会到。


弹性扩展


首先,大数据量级下你如何实现弹性扩展?因为我们今天主要讨论的是 OLTP ,是在线的存储服务,并不是离线分析的服务。所以在线的存储服务,它其实要做到的可用性、一致性,是要比离线的分析业务强得多的。但是在这种情况下,你们怎样做到业务无感知的弹性扩展,你的数据怎么很好的满足现有的高并发、大吞吐,还有数据容量的方案。


可用性


第二,在分布式的存储系统下,你的应用的可用性到底是如何去定义,如何去保证?其实这个也很好理解,因为在大规模的分布式系统里面,任何一个节点,任何一个数据中心或者支架都有可能出现硬件的故障,软件的故障,各种各样的故障,但这个时候你很多业务是并没有办法停止,或者并没有办法去容忍 down time 的。所以在一个新的环境之下,你如何对你系统的可用性做定义和保证,这是一个新的课题。一会儿我会讲到最新的研究方向和成果。


可维护性


第三,对于大规模的分布式数据库来说它的可维护性,这个怎么办?可维护性跟单机的系统是明显不同的,因为单机的数据库,或者传统的单点的数据库,它其实做到主从,甚至做到一主多从,我去维护 master ,别让它挂掉,这个维护性主要就是维护单点。在一个大规模的分布式系统上,你去做这个事情是非常麻烦的。可以简单说一个案例,就是 Google 的 Spanner。Spanner 是 Google 内部的一个大规模分布式系统,整个谷歌内部只部署了一套,在生产环节中只部署了一套。这一套系统上有上万甚至上数十万的物理节点。但是整个数据库的维护团队,其实只有很小的一组人。想像一下,上十万台的物理节点,如果你要真正换一块盘、做一次数据恢复或者人工运维的话,这是根本不可能做到的事情。但是对于一个分布式系统来说,它的可维护性或者说它的维护应该是转嫁给数据库自己。


开发复杂度


还有,就是对于一个分布式数据库来说,它在开发业务的时候复杂度是怎么样的。大家其实可能接触的比较多的,像 Hbase、Cassandra、Bigtable 等这种开源的实现,像 NoSQL 数据库它其实并没有一个很好的 cross-row transaction 的 support。另外,对于很多的 NoSQL 数据库并没有一个很好的 SQL 的 interface,这会让你写程序变得非常麻烦。比如说对于一些很普通的业务,一个表,我需要去 select from table,然后有一个 fliter 比如一个条件大于 10,小于 100,这么简单的逻辑,如果在 HBase 上去做的话,你要写十行、二十行、三十行;如果你在一个关系的数据库,或者支持 SQL 的数据库,其实一行就搞定了。其实这个对于很多互联网公司来说,在过去的几年之内基本上已经完成了这种从 RDBMS 到 NoSQL 的改造,但是这个改造的成本和代价是非常非常高的。比如我原来的业务可能在很早以前是用 MySQL 已经写的稳定运行好久了,但是随着并发、容量、可扩展性的要求,我需要迁移 Bigtable、Hbase、Cassandra、MongoDB 这种 NoSQL 数据库上,这时基本上就要面临代码的完整重写。这个要放在互联网公司还可以,因为它们有这样的技术能力去保证迁移的过程。反正我花这么多钱,招这么牛的工程师,你要帮我搞定这个事情。但是对于传统的行业,或者传统的机构来说,这个基本上是不可能的事情。你不可能让他把原来用 Oracle 用SQL 的代码改成 NoSQL 的 code。
因为 NoSQL 很少有跨行事务,首先你要做一个转账,你如果不是一个很强的工程师,你这个程序基本写不对,这是一个很大的问题。这也是为什么一直以来像这种 NoSQL 的东西并没有很好的在传统行业中去使用的一个最核心的原因,就是代价实在太大。


存储系统的扩展模型


所以其实在去讲这些具体到底该怎么解决,或者未来数据库会是什么样的之前,我想简单讲一下扩展的模型。对于一个关系型数据库也好,对于存储的系统本身也好,它的扩展模型有哪些。


Sharding 模式


第一种模式是 Sharding 模式。如果在座的各位有运维过线上的 MySQL 的话,对这个模型会非常熟悉。最简单的就是分库、分表加中间件,就是说我不同的业务可能用不同的库,不同的表。当一个单表太大的时候,我通过一些 Cobar、Mycat 等这样的数据库中间件来去把它分发到具体的数据库的实例上。这种模型是目前用的最普遍的模型,它其实也解决了很大部分的问题。为什么这十年在关系型数据库上并没有很好的扩展方案,但是大家看上去这种业务还没有出现死掉的情况,就是因为后面有各种各样 Sharding 的中间件或者分库分表这种策略在硬扛着。像这种中间件 Sharding 第一个优势就是实现非常简单。你并不需要对数据库内做任何的改造,你也并不需要去比如说从你原来的 SQL 代码转到 NoSQL 的代码。
但是它也有自己的缺点。首先,对你的业务层有很强的侵入性。这是没有办法的,比如你想用一个中间件,你就需要给它指定一个 Sharding key。另外,原来比如你的业务有一些 join ,有一些跨表跨行的事务,像这种事务你必须得改掉,因为很多中间件并没有办法支持这个跨 shard 的分布式 join。
第二个比较大的缺陷是它的分片基本是固定的,自动化程度、扩展性都非常差,你必须得有一个专职的 DBA 团队给你的 MySQL 或者 PG 的 Sharding 的集群去做运维。我之前在豌豆荚做过一段时间 MySQL cluster 的分片的维护工作。当时我记得是一个 16 个节点的 MySQL 的集群,我们需要扩展到 32 个节点的规模,整整提前演练了一个月,最后上线了一个礼拜。上线那个礼拜,晚上基本上没有办法睡觉,所以非常痛苦。
再说一个 Google 的事情,Google 在刚才我说的 Spanner 和 F1 这两个数据库没有上线之前,Google 的广告系统的业务是由 100 多个节点的 MySQL 的集群对外提供服务的。如 Google 这么牛的公司,在维护一百多个节点的 MySQL Sharding 的数据库的时候,都已经非常痛苦,宁可重新去写一个数据库,也不想去维护这个 datebase cluster。其实大家可以看到,像这种 Sharding 的方案,它比较大的问题就是它的维护代价或者维护集群的复杂度,并不是随着节点数呈线性增长,而是随着节点的增加非线性的增长上去。比如你维护 2 个节点的还好,维护 4 个节点的也还可以,但是你维护 16 个、64 个、128 个基本就是不可能的事情。
第三就是一些复杂的查询优化,并没有办法在中间件这一层,去帮你产生一个足够优化的执行计划,因此,对于一些复杂查询来说,Sharding 的方案是没法做的。所以对你的业务层有很高的要求。


Region Base 模型


第二种扩展模型是 Region Base。这张图是我项目里面扒出来的图。
分布式数据库模式与反模式 图1.png-296.9kB
它整个思路有点像 Bigtable,它相当于把底下的存储层分开,数据在最底层存储上已经没有表、行这样结构的划分,每一块数据都由一个固定的 size 比如 64 M、128 M 连续的 Key-value pairs 组成。其实这个模型背后最早的系统应该是谷歌在 06 年发表的 Bigtable 这篇论文里面去描述的。这个模型有什么好处呢?一是它能真正实现这种弹性的扩展。第二个,它是一个真正高度去中心化。去中心化这个事情,对于一个大的 Cluster 来说是一个非常重要的特性。
还有一个优势,在 KV 层实现真正具有一定的自动 Failover 的能力。 Failover指的是什么呢?比如说在一个集群比较大的情况下,或者你是一个 cluster,你任何一个节点,任何一个数据损坏,如果能做到业务端的透明,你就真正实现了 Auto-Failover 的能力。其实在一些对一致性要求不那么高的业务里面,Auto-Failover 就是指, 比如在最简单的一个 MySQL 组从的模型里,当你的组挂掉了以后,我监控的程序自动把 slave 提上来,这也是一种 Failover 的方式。但是这个一致性或者说数据的正确性并不能做到很好的保证。你怎么做到一致性的 Auto-Failover,其实背后需要做非常非常多的工作。
这是 Region 模型的一些优势。但是它的劣势也同样明显,这种模型的实现非常复杂。我一会儿会说到背后的关键技术和理论,但是它比起写中间件真的复杂太多了。你要写一个能用的 MySQL 或者 PG 的中间件,可能只需要一两个工程师,花一两周的时间就能写出一个能用的数据库中间件;但是你如果按照这个模型做一个弹性扩展的数据库的话,你的工作量就会是数量级的增加。
第二个劣势就是它业务层的兼容性。像 Region Base 的模型,最典型的分布式存储系统就是 HBase。HBase 它对外的编程接口和 SQL 是千差万别,因为它是一个 Key Value 的数据库。你的业务层的代码兼容性都得改,这个对于一些没有这么强开发能力的用户来说,是很难去使用的,或者它说没有 SQL 对于用户端这么友好。


可用性级别


我一会儿会讲一下,刚才我们由 Region Base 这个模型往上去思考的一些东西,在此之前先说一些可用性。高可用。其实说到高可用这个词,大多数的架构师都对它非常熟悉。我的系统是高可用的,任何一个节点故障业务层都不受影响,但是真的不受影响吗?我经过很多的思考得到的一个经验就是主从的模型是不可能保证同时满足强一致性和高可用性的。可能这一点很多人觉得,我主从,我主挂了,从再提起来就好,为什么不能保护这个一致性呢?就是因为在一个集群的环境下,有一种故障叫脑裂。脑裂是什么情况?整个集群是全网络联通的,但是出现一种情况,就是我只是在集群内部分成了两个互不联通的一个子集。这两个子集又可以对外提供服务,其实这个并不是非常少见的状况,经常会发生。像这种情况,你贸然把 slave 提起来,相当于原来的 master 并没有完全的被 shutdown,这个时候两边可能都会有读写的情况,造成数据非常严重的不一致,当然这个比较极端了。所以你会发现阿里或者说淘宝,年年都在说我们有异地多活。但是去年甚至前几个月,杭州阿里的数据中心光纤被挖断,支付宝并没有直接切到重复层,而是宁可停止服务,完全不动,也不敢把 slave 数据中心提起来。所以其实任何基于主从模型的异地多活方案都是不行的。这个问题有没有办法解决呢?其实也是有的。
还是说到 Google,我认为它才是全世界最大的数据库公司,因为它有全世界最大的数据量。你从来没有听说过 Google 哪一个业务因为哪一个数据中心光纤挖断,哪一个磁盘坏了而对外终止服务的,几乎完全没有。因为 Google 的存储系统大多完全抛弃了基于主从的一致性模型。它的所有数据都不是通过主从做复制的,而是通过类似 Raft 或者 Paxos 这种分布式选举的算法做数据的同步。这个算法的细节不展开了,总体来说是一个解决在数据的一致性跟自动的数据恢复方面的一个算法。同时,它的 latency 会比多节点强同步的主从平均表现要好的一个分布式选举的算法。
在 Google 内部其实一直用的 Paxos,它最新的 Spanner 数据库是用 Paxos 做的 replication 。在社区里面,跟 Paxos 等价的一个算法就是 Raft。Raft 这个算法的性能以及可靠性都是跟 Paxos 等价的实现。这个算法就不展开了。我认为这才是新一代的强一致的数据库应该使用的数据库复制模型。


分布式事务


说到事务。对于一个数据库来说,我要做传统的关系型数据库业务,事务在一个分布式环境下,并不像单机的数据库有这么多的方法,这么多的优化。其实在分布式事务这个领域只有一种方法,并且这么多年了从分布式事务开始到现在,在这个方法上并没有什么突破,基本只有一条出路就是两阶段提交。其实可以看一下 Google 的系统。对于我们做分布式系统的公司来说,Google 就是给大家带路的角色。Google 最新的数据库系统上它使用的分布式事务的方法仍然是两阶段提交。其实还有没有什么优化的路呢?其实也是有的。两阶段提交最大的问题是什么呢?一个是延迟。因为第一阶段先要把数据发过去,第二阶段要收到所有参与的节点的 response 之后你才能去 commit 。这个过程,相当于你走了很多次网络的 roundtrip,latency 也会变得非常高。所以其实优化的方向也是有的,但是你的 latency 没法优化,只能通过吞吐做优化,就是 throughput 。比如说我在一万个并发的情况下,每个用户的 latency 是 100 毫秒,但是一百万并发,一千万并发的时候,我每个用户的 latency 还可以是 100 毫秒,这在传统的单点关系型数据库上,是没有办法实现的。第二就是去中心化的事务管理器。另外没有什么东西是银弹,是包治百病的,你要根据你的业务的特性去选择合适的一致性算法。


NewSQL


其实刚刚这些 pattern 会发展出一个新的类别,我们能不能把关系数据库上的一些 SQL、Transaction 跟 NoSQL 跟刚才我说到的 Region Base 的可扩展的模型融合起来。这个思想应该是在 2013 年左右的时候,学术界提出来比较多的东西,NewSQL。NewSQL 首先要解决 Scalability 的问题, 刚给我们说过 scalability 是一个未来的数据库必须要有的功能,第二个就是 SQL,SQL 对于业务开发者来说是很好的编程的接口。第三,ACID Transaction,我希望我的数据库实现转帐和存钱这种强一致性级别的业务。第四,就是整个 cluster 可以支持无穷大的数据规模,同时任何数据节点的宕机、损坏都需要集群自己去做监控,不需要 DBA 的介入。


案例:Google Spanner / F1


有没有这样的系统?其实有的。刚才一直提到 Google 的 Spanner 系统。Spanner 系统是在 2012 年底于 OSDI 的会议上发布了论文; F1 这篇论文在 2013 年的 VLDB 发布的,去描述了整个 Google 内部的分布式关系型数据库的实现。首先,根据 Spanner 的论文 Spanner 和 F1 在生产环境只有一个部署,上万物理节点遍布在全球各种数据中心内,通过 Paxos 进行日志复制。第二,整个架构是无状态的 SQL 层架在一个 NoSQL 的基础之上。第三,它对外提供的是一个跨行事务的语义,这个跨行事务是透明的跨行事务,我不需要对我的业务层做修改,它是通过 一个硬件支持的 Truetime API,GPS 时钟和原子钟去实现事务的隔离级别。这个系统是最早用来支撑 Google 的在线广告业务,在线广告业务大家知道,其实对于扣费、广告计费、点击记录,广告活动等一致性的级别要求非常高。首先这个钱不能多扣,也不能少扣,实时性要求非常高。而且业务逻辑相当复杂,这个时候需要一个 SQL 数据库,需要一个支持事务的数据库,需要一个支持 Auto-Failover 的数据库,所以就有了 Google Spanner/F1 这个系统。


典型业务场景


最典型的业务场景是什么呢?对于这种高吞吐、大容量的数据量级对于 NoSQL 系统来说,典型的应用的 Pattern 就是高吞吐大容量,还有就是 Workload 相对比较分散的情况。比较典型的反例是秒杀,秒杀这个场景其实非常不适合这种 NewSQL。另外就是一致性、可用性和 latency 之间怎么做取舍。在这种分布式数据库上面,第一个要丢弃的就是延迟,在这么大规模的量级上做强一致性,延迟是首先是不能保证的。你去看谷歌的 F1 和 Spanner 的论文里面,它提到它们的 latency 基本都是 10 毫秒、20 毫秒、甚至50 毫秒、100 毫秒的量级,但是它并不会太关心 latency 。因为它必须保证通过 Paxos 去做跨机房、多机房的复制,光速你肯定没法超越,所以这个时间是省不了的,它整个系统是面向吞吐设计的,而不是面向低延迟设计的,但是它要求非常强的一致性。


MySQL Sharding


一个典型的场景就是替换 MySQL Sharding 的业务。它的业务典型的特点,就是高吞吐,海量并发的小事务,并不是特别大的 transection,模型也相对简单,没有复杂的 join 的程序。但是痛点刚才提到了非常明显,首先就是对于 MySQL Sharding 方案 scale 能力很差,对于表结构变更的方案并没有太多;再者,cross shard transaction,目前 Sharding 的方案并没有办法很好支持,但 NewSQL 面向的场景和 MySQL Sharding 面向的场景是非常的像的。


Cross datacenter HA


第二种场景是 Cross datacenter HA(跨数据中心多活),这种场景简直是数据开发者追求的圣杯。因为像 Oracle、DB2文,它在最关键,最核心的业务上,比如说像银行这种业务,它必须要求实现这种跨数据中心的高可用。但是在一个分布式的数据库上,或者说是 Open-source solution 里,有没有人有办法实现这个 Cross datacenter HA 呢?完全没有。目前来说并没有任何一个数据库能去解决这个问题。因为刚才提到了主从的模型根本是不可靠的。像这种业务的数据极端的重要,任何一点数据的丢失都不能容忍。另外即使整个数据中心宕机也不能影响线上的业务,很典型的像支付宝,这种涉及钱相关的,甚至有些比如你的银行卡,你肯定不能容忍说你吃完饭,告诉你不好意思数据中心挂了,你的钱我刷不了。但是并没有开源的数据库能解决这个问题,如果你真的自己做同步复制的方案的话,特别两地三中心的情况,你请求的延迟是取决于离你最远的数据中心,所以大部分业务来说延迟过大。 另外就是这些系统重度依赖人工运维。我认为一旦任何系统有人工的介入一定是会出错的。因为人是最难自动化的一个因素。


反模式


滥用传统关系模型


对于这种大规模的分布式 NewSQL 来说,比较大的反模式就是,大量的使用存储过程 foreign key,视图等操作。因为首先存储过程是没有办法 scale 的。另外,大表与大表的 JOIN ,在线上的 OLTP 数据库上最大的开销并不是优化器的开销,而是网络通信的开销。比如两个表去做一个笛卡儿积,算起来可能并不是这么慢,但是要把数据一条一条在网络中传输代价是非常大的,这种情况下用 OLAP 的数据库是比较适合。


没有利用好并发


刚才我一直强调对于新型的 OLTP 或者分布式关系型数据库来说,并发或者说吞吐才是应该追求的优化点。在架构设计时,不应该把对延迟非常敏感的业务去使用分布式数据库来实现。所以其实在延迟跟吞吐之间,数据库永远去选择吞吐。所以写程序会遇到的一个很典型的问题,就是我的查询是一行一行,上下之间有这种强依赖关系的模式。其实在这种模式下,对于分布式关系型数据库来说是非常不合适的。所以你应该用并发的思想去做,比如说我的请求,我可以把吞吐打到整个集群上,我的 Database,我的 cluster 会自动 balance 一些 workload,如果一行行查询之间是有依赖的话,那么每一条查询之间的 latency 是会叠加起来。所以这个其实并不是一个太好的 pattern 。


不均匀的设计


数据库其实经常会被大家滥用。比如说我看到很多传统行业数据库使用的方法,其实真的是非常痛苦。无论什么业务我全都直接上 Oracle,不管这个业务是否真的需要 Database 来去支持;比如有人用 MySQL 做计数器,有些有 MySQL 做队列或者做秒杀,这个并不是太 scale 的东西。特别像秒杀的业务,特别高的并发打到同一个 key 上,workload 没办法去分拆到好多个节点来帮你去处理,所以整个分布式的集群就会退化成一个单点,这是非常不合理的。第二,当你的索引设计的不太好的时候,涉及到的过多全表扫描是非常不划算的。刚刚也说过,在一个分布式集群里面,最大的开销是网络的开销,你做全表扫描的时候并不是在一台机器上,而是在好多机器上做全表扫描,同时经过很大的网络传输,当你的索引设计不合理的时候会出现这样的问题。还有是过多的无效索引,会导致整个系统在写入的时候有延迟也好或者吞吐也好,会变得更慢。主要注意的就是不要存在任何可以把你的系统退化成单点的机会。


错误的一致性模型


还有一种最容易犯的问题,就是在没有好好思考你的业务适合的场景情况下,就去使用很强的一致性模型。因为当你要求一个非常高的强一致性的时候,你的分布式事务的 latency 一定比不这么强的一致性的业务要高得多的。其实,很多业务并不需要那么强的一致性,我的数据库虽然给你了这个能力,但如果你去滥用的话,你会说这个数据库好慢,或者为什么我这个请求 100 毫秒才给我返回。但其实很多场景下,你并不是这么强的依赖强一致性 。这个跟数据库本身提不提供这个机制是没有关系的。作为一个 Database 开发者来说,我还是需要给你能实现这个功能的机制,但是不要滥用。
另外根据你业务的冲突的频繁程度选择不同的锁策略。你知道这个业务就是一个很高的冲突的场景,比如说可能像类似发工资,一个集团的账号发到成千上万的小账号。如果你是一个乐观事务的话,可能涉及到的冲突就会很大。因为所有的事务的发起者都要去修改这个公共账号,这种情况下一般可以使用悲观事务,因为你已经预知到你业务的 conflict 级别会很高。但当我知道我的业务的冲突很小,我要追求整个系统的吞吐,我可能会选用乐观的事务的模型。


Q&A


问:对于秒杀这个场景,什么样的数据库设计是合适的?



黄东旭:其实秒杀这个业务是一个比较系统的工程,并不是一个简单的我用什么数据库就可以做的东西。我之前给一些朋友做秒杀的架构设计的时候,从最上层,甚至你可能从 JS 这边就要去做排队,一层一层的排队。比如在缓存这边我可以用 redis,我可以用一些缓存的数据库再去进行二级的排队,因为每一次排队你都会丢掉很多,最后到底下的队列,在数据库这一层,比如库存就十个,你只要在前面放十个进来就行了。就是说,你在上层把流量用排队的方式做更好。最典型的例子就是大家看 12306 卖火车票的时候经常会有排队。基本没有什么数据库能去应对,像淘宝,像京东这种秒杀的业务,它从上到下是整个系统的过程。谢谢。



问:分布式的 OLTP 数据库,历史数据怎么清理,如果后续还需要分析数据的话。



黄东旭:其实在谷歌 Spanner 包括像 TiDB 、TiKV 这个系统之上,是通过一种 MVCC 的技术,每一条数据都会有一个版本号。比如说你不停的 update,它可能会产生好多版本。我背后会有一个 GC 的逻辑,去选定一个 safe point,在这个 safe point 之前的所有数据全都删掉也好,还是挪到冷的存储上也好,我们是通过这种方式实现的。另外随着数据量的膨胀,整个系统设计的时候,我们是通过 Raft 算法,比如说这个数据慢慢长大,我会把它切成两块,再慢慢 Auto-balance 到其它的节点上,然后新的那一块会再长大......这相当于一个细胞一样,一个细胞分裂成两个细胞,成千上万的细胞会均匀的分布在整个集群里面。如果你要删数据可以直接删,如果你不想删的话,你的数据可以全都留下,你自己随便往集群里增加新的节点。这个集群很大,它会自动帮你把数据均匀的分到这儿。所有的历史数据是通过 GC 的模块把历史的版本拿出来,丢到冷的存储上。
在 Google 几乎是完全不删数据的,因为存储的成本是很低的,但是数据未来说不定什么时候就用了。大概是这样,我需要有一个办法能把它存下来。



问:当前做的交易要使用历史的参考数据,可能量不是很大,有这样的例子吗?



黄东旭:当然有。像 MVCC 在访问历史,它其实提供了一个 Lock-free 的 Snapshot Read, 它在读历史版本的时候是不会读线上的正在进行的读写事务,所以它是有一个无所读的机制,这也是为什么它要去采用每一个数据都要有版本号的机制去实现。它是有这个需求,而且在实现的时候也非常漂亮,不会阻塞其他的请求。



问:TiDB 是怎么考虑用 MySQL 或者 PG 的?当时是怎么考虑的?



黄东旭:首先我们没有用 MySQL 或者 PG,刚才说的 TiDB 这个模型是谷歌的 Spanner 跟 F1 的模型。用传统的单机数据库做改造的话,有一个比较大的误区,整个 SQL 的查询优化器跟存储的数据结构,并不知道你底层是分布式的存储,它还是假设你生成 SQL 的方案都是单机的。比如最简单的例子,我需要去 count ,如果是一个单机的 MySQL 优化器,没有建索引的话会一行一行把数据拉过来计一个数,这样算下去;但是如果对一个分布式系统来说,我只要把 count 这个逻辑推到所有存储表的数据节点上,算法再 reduce 回来就可以,更像一个 Mapreduce 这种分布式框架的 SQL 优化器。如果你没有从底到上完整的去实现 Database 的话,你很难对分布式的场景或者分布式数据存储的这些性质来去对数据库做优化。没有办法,这是一条很难的道路,但是我们也得去下走。



原文链接

云时代数据库的核心特点

文章分享qiuyesuifeng 发表了文章 • 0 个评论 • 367 次浏览 • 2016-10-11 15:06 • 来自相关话题

引言

最近几年,随着云计算相关技术的发展,各种不同类型的云层出不穷,服务越来越多不同类型的企业业务,传统企业也渐渐开始探索上云的道路。在云上,作为业务最核心的数据库,相比之前的传统方案会有哪些变化呢?在正式... 查看全部

引言


最近几年,随着云计算相关技术的发展,各种不同类型的云层出不穷,服务越来越多不同类型的企业业务,传统企业也渐渐开始探索上云的道路。在云上,作为业务最核心的数据库,相比之前的传统方案会有哪些变化呢?在正式聊云时代的数据库特点之前,我们需要了解一下目前云时代架构发生的变化。


畅想一下,未来的服务都跑在云端,任何的服务资源都可以像水电煤一样按需选购。从 IaaS 层的容器/虚拟机,到 PaaS 层的数据库,缓存和计算单元,再到 SaaS 层的不同类型的应用,我们只需要根据自身业务特点进行资源选配,再也不用担心应用服务支撑不住高速的业务增长,因为在云上一切都是弹性伸缩的。有了可靠的基础软件架构,我们就可以把更多精力放到新业务的探索,新模式的创新,就有可能产生更多不一样的新场景,从而催生更强大能力的云端服务,这是一件多么 cool 的事情。


当然,理想要一步一步实现,未来的基础软件栈到底会怎样呢?社区在这方面正在进行积极地探索,其中最有代表性的就是基于容器(以 Docker 为代表)的虚拟化技术和微服务(Microservice)。


在云时代,一切都应该是可伸缩的,使用 k8s(Kubernetes)在保证资源平衡的前提下,通过 Docker 部署我们依托于容器的微服务模块,我们不用关心服务到底跑在哪里,只需要关心我们需要多少服务资源。Docker 提供了极大的便利性,一次构建,到处运行,我们可以很好地解决开发、测试和上线的环境一致性问题。(如果不能很好地保证测试和实际上线环境的一致性,则很有可能需要花费远超过开发的时间去发现和修复问题。)k8s 更是在 Docker 构建的基础上增加了更多的云特性,包括 Docker 的升级,高可用和弹性伸缩等等。 关于 Docker/k8s 相关的讨论已经很多了,因为时间关系,关于具体的细节就不再展开。我们只需要了解,有了它,可以很轻松地解决服务的安装和部署。


下面再聊聊微服务,微服务将一个服务拆分成相对独立的更小的子服务单元,不同的子服务单元之间通过统一的接口(HTTP/RPC 等)进行数据交互。


相比于传统的解决方案,这种架构有很多的优点。



  • 更好的开发效率和可维护性。微服务将一个单独的服务进行更细力度的拆分,每一个子服务单元专注于更小的功能模块,可以更好地根据业务建立对应的数据模型,降低复杂度,使得开发变得更轻松,维护和部署变得更加友好.

  • 更好的可扩展性。每个不同的子服务单元相互独立,彼此之间没有任何依赖,所以可以根据业务的具体需要,灵活地部署多个子服务单元进行水平扩展。

  • 更强的容错性。当其中一个子服务出现故障的时候,可以通过辅助的负载均衡工具,自动路由到其他的子服务,不会影响整体服务的可用性.


当然,微服务也不是一个银弹,相对来说,这种方案会使整体系统的设计更加复杂,同时也加大了网络的延迟,对整个系统测试的复杂度也会更高。


Docker 提供的隔离型和可移植性,与微服务是一种天然的契合,微服务将整个软件进行拆分和解耦,而通过 Docker/k8s 可以很自然地做到独立的部署,高可用和容错性,似乎一切都可以完美地运转起来。但是真的是这样么?我们是不是忽略了什么?


是的,我们在讨论前面的问题的时候忽略了一个很重要的东西:状态。


从整个技术发展的角度来看,微服务是一个非常有意义的探索。每个人都期望着每个微服务的子服务都是无状态的,这样我可以自由地启停和伸缩,没有任何的心智负担,但是现实的业务情况是什么样的呢?比如一个电商网站,用户正在下单购买一件商品,此时平台是通过订单子服务的 A 应用来提供服务的,突然,因为机器故障,订单子服务的 A 应用不可用了,改由订单子服务的 B 应用提供服务,那么它是必须要知道刚才用户的订单信息的,否则正在访问自己订单页面的用户会发现自己的订单信息突然不见了。虽然我们尽量想把子服务设计成无状态的,但是很多时候状态都是不可避免的,我们不得不通过存储层保存状态,业界最主要的还是各种数据库,包括 RDBMS 和 NoSQL,比如使用 MySQL、MongoDB、HBase、Cassandra 等,特别是有些场景还要考虑数据一致性问题的时候,更加重了对存储层的依赖。


由此可见,云计算时代系统的架构发生了巨大的变化,这一方面为用户提供了更优秀的特性,另一方面也对云计算的组件提出了更高的要求。数据库作为云计算最基础的组件之一,也需要适应这种架构的变化。(这里我们主要关注 SQL 数据库,云时代的数据库以下简称云数据库。)


那么云数据库主要有一些什么样的特点呢?我认为主要有以下几点。


弹性伸缩


传统的数据库方案,常见的会选用 Oracle,MySQL,PostgreSQL。在云时代,数据量的规模有爆发性的增长,传统的数据库很容易遇到单机的存储瓶颈,不得不选用一些集群方案,常见的比如 Oracle RAC、 MySQL Sharding 等,而这些集群方案或多或少都有一些不令人满意的地方。


比如说,Oracle RAC 通过共享存储的硬件方案解决集群问题,这种方式基本上只能通过停机换用更大的共享内存硬件来解决扩容问题,RAC 节点过多会带来更多的并发问题,同样也会带来更高的成本。


以 MySQL Sharding 为代表的数据分片方案,很多时候不得不提前对数据量进行规划,把扩容作为很重要的一个计划来做,从 DBA 到运维到测试到开发人员,很早之前就要做相关的准备工作,真正扩容的时候,为了保证数据安全,经常会选择停服务来保证没有新的数据写入,新的分片数据同步后还要做数据的一致性校验。当然业界大公司有足够雄厚的技术实力,可以采用更复杂的方案,将扩容停机时间尽量缩短(但是很难缩减到 0),但是对于大部分中小互联网公司和传统企业,依然无法避免较长时间的停服务。


在云时代,理想中所有的资源都是根据用户业务需求按需分配的,服务器资源,应用容器资源,当然也包括数据库资源。添加或者减少新的数据库资源,完全就像日常吃饭那样稀疏平常,甚至用户基本感知不到。比如作为一个电商用户,在双 11 促销活动之前,可以通过增加数据库节点的方式,扩大更多的资源池,用来部署相应的容器服务,当活动结束之后,再将多余的资源移除去支持其他的服务,这样可以极大地提高资源的利用率,同样可以弹性地支撑各种峰值业务。


高可用


传统的 MySQL 方案,数据复制的时候默认采用异步的方式,对于一个写入的请求,主库写入成功后就会返回成功信息给客户端,但是这个时候数据可能还没有同步给从库,一旦主库这个时候挂掉了,启动从库的时候就会有丢失数据的风险。当然,也有人会选择半同步的复制方式,这种方式在正常情况下是同步的,但是在遇到数据压力比较大的时候,依然会退化为异步的方式,所以本质上来说,同样有丢失数据的风险。其他也有一些多主的同步方案,比如在应用层做数据同步,但是这种方式一是需要应用层的配合,二是在对网络超时的处理非常复杂,增加心智负担。


在云时代,因为所有的数据库资源都是分布式存储的,每个数据库节点出现问题都是很正常的事情,所以就必须有一种可以实现数据一致性的数据复制方式来保证服务的高可用,业界给出的答案就是:Paxos/Raft(关于 Paxos 和 Raft 的实现细节我们不在这里展开)。PingCAP 在做的 TiDB 就是选择了 Raft 协议,Raft 协议看起来更像是一个多副本的自适应的主从复制协议,对于每次写请求,Raft 都会保证大多数写成功才会返回客户端,即使 Raft Group的Leader 挂掉了,在一个有限的时间范围内,会很快地选出一个新的 Leader 出来,继续提供服务。同样,对于一个 3 副本的 Raft Group,只要 2 个写入成功,就可以保证成功,而大多数情况下,最先写入成功的往往是与 Leader 网络情况最好的那个副本,所以这种 Majority 写的方式,可以很自然地选择速度最快的副本进行数据同步复制。另外,Raft 协议本身支持 Config Change,增加一个新的节点,可以很容易地做副本数据分布的变更,而不需要停止任何服务。


同样,在云时代,数据库的 DDL 操作也会是一个非常有趣的事情。以一个常见的 Add Column 操作为例,在表规模已经很大的情况下,在传统的实现方案中,比较有参考意义的是,通过一些工具,创建类似表级别的触发器,将原表的数据同步到一个新的临时表中,当数据追平的时候,再进行一个锁表操作,将临时表命名为原表,这样一个 Add Column 操作就完成了。但是在云时代,分布式的数据存储方式决定了这种方案很难实现,因为每个数据库节点很难保证 Schema 状态变更的一致性,而且当数据规模增长到几十亿,几百亿甚至更多的时候,很短的阻塞时间都有可能会导致很大的负载压力变化,所以 DDL 操作必须是保证无阻塞的在线操作。值得欣慰的是,Google 的 F1 给我们提供了很好的实现参考,TiDB 即是根据 F1 的启发进行的研发,感兴趣的同学可以看下相关的内容。


易用透明

我们可以将云数据库想象成一个提供无限大容量的数据库,传统数据库遇到单机数据存储瓶颈的问题将不复存在。已有的程序基本上不怎么需要修改已有的代码,就可以很自然地接入到云数据库中来获得无限 Scale 的能力。增减数据库节点,或者节点的故障恢复,对于应用层来说完全透明。另外,云数据库的监控、运维、部署、备份等等操作都可以在云端通过高效的自动化工具来自动完成,极大地降低了运维成本。


多租户

云数据库本身应该是可以弹性伸缩的,所以很自然的,从资源利用率的角度来考虑,多个不同用户的数据库服务底层会跑在一个共享的云数据库中。因此多租户技术会成为云数据库的标配。但是这里面就有一个不得不面对的问题,如何做到不同用户的隔离性?用户数据隔离是相对比较容易的,比如还是以电商用户(这里说的是电商企业,不是顾客客户)为例,每个用户都有一个唯一的 ID,这样在云数据库的底层存储中,可以保证每个用户数据都带有自己 ID 前缀,用户登陆进来的时候可以根据这个前缀规则,获取他对应的数据,同时他看不到其他用户的数据。


在一个真实的多租户环境下面,纯粹的数据隔离往往是不够的,你还需要做到资源公平性的隔离。比如有的用户写一个 SQL,这个 SQL 没有做优化,主要做的事情是一个全表描扫,这个表的数据量特别特别大,这样他会吃掉很多的 CPU、Memory、IO 等资源,导致其他用户很轻量级的 SQL 操作都可能会变得很慢,影响到其他用户实际的体验。那么针对这种情况怎么做隔离?与此类似的还有,网络带宽怎么做隔离?大家都是跑在一个云数据库上面的,如果一个用户存放的数据特别大,他把带宽都吃掉了,别人就显得非常慢了。


还有一种情况,如果我本身作为一个租户,内部又怎么做隔离,大家知道 MySQL 可以建很多 Database,不同的 Database 给不同的团队来用,那么他们之间内部隔离又怎么做,这个问题就进一步更加复杂了。


目前来讲没有特别好的方法,在一个分布式的环境下面去做很好的隔离,有两个方向可以考虑:


第一种是最简单也是有效的方法,制定一些规则,把某些用户特别大的数据库表迁移到独享的服务器节点上面,这样就不会影响其他用户的服务,但是这里面就涉及到定制化的事情了,本身理念其实与云数据库并不相符。


第二种就是依靠统计信息,做资源隔离和调度,但是这里面对技术的要求就比较高了。因为云数据库是分布式的,所以一般的统计都要横跨很多的机器,因为网络原因,不可能做到完全准确的统计,所有统计都是有延迟的。比如说对于某个用户,现在统计到的流量是 1 个 G,他可能突然就有一次峰值的网络访问,可能下一次统计消耗的流量是 5 个 G(这里面只是举例说明问题),如果你给他流量限制是 1 个 G,中间统计的间隔是多少比较合适,如果间隔比较小,那么这个对整个系统的压力就比较大,可能影响正常的用户 SQL 访问,另外本身这个流量限制的系统也是很复杂的系统。


调度算法一直是整个分布式系统领域很困难的一个问题,如何做到隔离性和公平调度也是未来云数据库非常有挑战的一个事情。


低成本

低成本应该是云时代基础设施最明显的特点。首先,云数据库的高可用和容错能力,使得我们不再需要昂贵的硬件设备,只需要普通的 X86 服务器就可以提供服务。然后,受益于 Docker 的虚拟化技术,使得不同类型的应用容器可以跑在同一个物理机上,这样可以极大地提高资源的利用率。其次,多租户的支持,使得不同的用户可以共用一套底层的数据库存储系统,在数据库层面再一次提高了资源的利用效率。再次,云数据库的自动化运维工具,降低了整个核心数据库的运维成本。最后,云数据库资源是按需分配的,用户完全可以根据自身的业务特点,选购合适的服务资源。


高吞吐


云数据库虽然可以做到弹性扩容,但是本身是分布式存储的,虽然可以通过 Batch Write、Pipeline 和 Router Cache 等方式加快访问 SQL 请求的数据,但是相对传统单机的数据库来说,在数据访问链路上至少也要多走一次网络,所以大部分并发量不大的小数据量请求,都会比单机延迟要高一些。也就是说,当没有足够高的并发 SQL 访问的话,其实不能完全体现云数据库的性能优势,所以这也是我们在选用云数据库的时候需要认识到的问题,云数据库更多的是追求高吞吐,而不是低延迟。当并发大到一定规模,云数据库高吞吐特性就显现出来了,即使在很高的并发下,依然可以维持相当稳定的延迟,而不会像单机数据库那样,延迟线性增长。当然,延迟的问题,在合理的架构设计方案下,可以通过缓存的方式得到极大的缓解。


数据安全

云数据库的物理服务器分布在多个机房,这就为跨数据库中心的数据安全提供了最基础的硬件支持。谈到金融业务,大家耳熟能详的可能就是两地三中心,比如北京有两个机房,上海有一个。未来一切服务都跑在云上,金融类的业务当然也不例外。相比其他业务,金融类业务对数据安全要求就要高得多。当然,每个公司内部都有核心的业务,所以如果上云的话,也会有同样的强烈需要。这样,对云数据库来说,数据的一致性、分布式事务、跨数据中心的数据安全等更高端的需求有可能会日益强烈。常见的数据备份也有可能会被其他新的模式所取代或者弱化,比如基于 Paxos/Raft 的多副本方案,本身就保证了会有多份备份。


自动负载平衡

对于云数据库来说,负载平衡是一个很重要的问题,它直接决定了整个云数据库系统性能的好坏,如果一个数据库节点的数据访问过热的话,就需要考虑把数据迁移到其他的数据库节点来分担负载,不然就很容易出现性能瓶颈。整个负载平衡是一个动态的过程,调度算法需要保证资源配比的最大平衡,还有保证数据迁移的过程对系统整体的负载影响最小。这在未来也是云数据库需要解决的一个核心问题。


小结

从目前已有的 SQL 数据库实现方案来看,NewSQL 应该是最贴近于云数据库理念的实现。NewSQL 本身具有 SQL、ACID 和 Scale 的能力,天然就具备了云数据库的一些特点。但是,从 NewSQL 到云数据库,依然有很多需要挑战的难题,比如多租户、性能等。


上面提到的一些云数据库的特点,也是 PingCAP 目前在着力实现的部分,TiDB 作为国内第一个 NewSQL 的开源项目,在与社区的共同努力下,我们在上月底刚刚发布了 Beta 版本,欢迎各位上 GitHub 了解我们。


随着整个社区技术水平的发展和云时代新的业务需求的驱动,除了 PingCAP 的 TiDB,相信会有更多的团队在这方面进行探索, 期待早日看到云数据库成熟的那一天。


Q&A

问:由于客户数据环境复杂多样,在迁移到云端的时候怎么怎么做规划,以便后期统一运维管理?或者说,怎么把用户 SQL Server 或者 MongoDB 逐渐迁移到 TiDB 之类的分布式数据库?

崔秋:因为每个业务场景都不太相同,所以在选用云端服务的时候,首先要了解自身业务和云服务具体的优缺点。
如果你的业务本身比较简单,比如你之前用的 MongoDB,现在很多云服务厂商都会提供云端的 MongoDB 服务。这个时候你就要根据业务特点来做判断,如果 MongoDB 本身容量不大,远期的业务数据不会增长过快的话,这个时候其实你可以直接使用 MongoDB 的服务的。但是如果你本身的数据量比较大,或者数据增长比较快的话,就可能要考虑数据的扩容问题,MongoDB 在这方面做的不是太好。
你可以考虑 SQL 数据库的集群方案。比如 TiDB,它本身是支持弹性扩容,高并发高吞吐和跨数据库中心数据安全的,另外有一点明显的好处是 TiDB 兼容 MySQL 协议,所以如果你的应用程序是使用 MySQL,就基本上可以无缝地迁移到 TiDB,这方面是非常方便的。后续我们会提供常用的数据库迁移工具,帮用户把数据从 MongoDB/SQL Server 等平滑迁移到 TiDB 上面。
还是那个原则,不要为了上云而上云,一定要了解清楚自己的业务特点,云数据库会帮助你提供很多特性,如果真的很适用你的业务的话,就可以考虑。


问:但从产品的角度来看,云厂商提供的 RDS 产品是 Copy 客户数据库的思路,或者说是为了支持不同的数据库而支持。请问这种局面以后会有什么改变吗?

崔秋:现在确实蛮多云数据库服务其实就是在传统的 RDS 上面包了一层自动化的监控,运维和部署工具,就卖给用户提供服务了,但是实际上本身解决的仅仅是自动化管控的问题,云服务提供的数据库特性还是单机的 RDS,或者 RDS Sharing 的特性。如果本身底层的数据库满足不了你的需求的话,云服务也是满足不了的。
如果你需要不停服务的弹性扩容,单机的 RDS 服务肯定是搞不定的,RDS Sharing 也很难帮助你做到,这就对底层的数据库有了更高的要求,当然这方面是 TiDB 的强项了。
现在很多云上的 RDS 产品还远远没有达到理想中的云数据库的要求,不过随着社区的发展和业务需求的推动,我个人觉得,这方面最近几年会有更多的变化。如果对于这方面感兴趣的话,可以关注下 TiDB。


问:从 Oracle 分流数据到 TiDB、Oracle 增量修改、Update 的记录,如何同步到 TiDB?有没有工具推荐,比如类似 Ogg?


崔秋:目前 TiDB 还没有相应的工具。如果真的需要在线从 Oracle 这边分流的话,可以考虑使用 Oracle 的触发器,将数据的变化记录下来,然后转化为 SQL,同步到 TiDB,当然这需要一定开发的工作量。


原文链接

基于 Raft 构建弹性伸缩的存储系统的一些实践

文章分享qiuyesuifeng 发表了文章 • 0 个评论 • 447 次浏览 • 2016-10-11 15:00 • 来自相关话题

最近几年来,越来越多的文章介绍了 Raft 或者 Paxos 这样的分布式一致性算法,且主要集中在算法细节和日志同步方面的应用。但是呢,这些算法的潜力并不仅限于此,基于这样的分布式一致性算法构建一个完整的可弹性伸缩的高可用的大规模存储系统,是一个很新的课... 查看全部

最近几年来,越来越多的文章介绍了 Raft 或者 Paxos 这样的分布式一致性算法,且主要集中在算法细节和日志同步方面的应用。但是呢,这些算法的潜力并不仅限于此,基于这样的分布式一致性算法构建一个完整的可弹性伸缩的高可用的大规模存储系统,是一个很新的课题,我结合我们这一年多以来在 TiKV 这样一个大规模分布式数据库上的实践,谈谈其中的一些设计和挑战。


本次分享的主要内容是如何使用 Raft 来构建一个可以「弹性伸缩」存储。其实最近这两年也有很多的文章开始关注类似 Paxos 或者 Raft 这类的分布式一致性算法,但是主要内容还是在介绍算法本身和日志复制,但是对于如何基于这样的分布式一致性算法构建一个大规模的存储系统介绍得并不多,我们目前在以 Raft 为基础去构建一个大规模的分布式数据库 TiKV ,在这方面积累了一些第一手的经验,今天和大家聊聊类似系统的设计,本次分享的内容不会涉及很多 Raft 算法的细节,大家有个 Paxos 或者 Raft 的概念,知道它们是干什么的就好。


先聊聊 Scale


其实一个分布式存储的核心无非两点,一个是 Sharding 策略,一个是元信息存储,如何在 Sharding 的过程中保持业务的透明及一致性是一个拥有「弹性伸缩」能力的存储系统的关键。如果一个存储系统,只有静态的数据 Sharding 策略是很难进行业务透明的弹性扩展的,比如各种 MySQL 的静态路由中间件(如 Cobar)或者 Twemproxy 这样的 Redis 中间件等,这些系统都很难无缝地进行 Scale。


Sharding 的几种策略


在集群中的每一个物理节点都存储若干个 Sharding 单元,数据移动和均衡的单位都是 Sharding 单元。策略主要分两种,一种是 Range 另外一种是 Hash。针对不同类型的系统可以选择不同的策略,比如 HDFS 的Datanode 的数据分布就是一个很典型的例子:


WechatIMG12.png-50.3kB


首先是 Range


Range 的想法比较简单粗暴,首先假设整个数据库系统的 key 都是可排序的,这点其实还是蛮普遍的,比如 HBase 中 key 是按照字节序排序,MySQL 可以按照自增 ID 排序,其实对于一些存储引擎来说,排序其实是天然的,比如 LSM-Tree 或者 BTree 都是天然有序的。Range 的策略就是一段连续的 key 作为一个 Sharding 单元:


屏幕快照 2016-10-13 下午4.40.22.png-180.9kB


例如上图中,整个 key 的空间被划分成 (minKey, maxKey),每一个 Sharding 单元(Chunk)是一段连续的 key。按照 Range 的 Sharding 策略的好处是临近的数据大概率在一起(例如共同前缀),可以很好的支持 range scan 这样的操作,比如 HBase 的 Region 就是典型的 Range 策略。


但是这种策略对于压力比较大的顺序写是不太友好的,比如日志类型的写入 load,写入热点永远在于最后一个 Region,因为一般来说日志的 key 基本都和时间戳有关,而时间显然是单调递增的。但是对于关系型数据库来说,经常性的需要表扫描(或者索引扫描),基本上都会选用 Range 的 Sharding 策略。


另外一种策略是 Hash


与 Range 相对的,Sharding 的策略是将 key 经过一个 Hash 函数,用得到的值来决定 Sharding ID,这样的好处是,每一个 key 的分布几乎是随机的,所以分布是均匀的分布,所以对于写压力比较大、同时读基本上是随机读的系统来说更加友好,因为写的压力可以均匀的分散到集群中,但是显然的,对于 range scan 这样的操作几乎没法做。


屏幕快照 2016-10-19 下午6.14.37.png-135.4kB


比较典型的 Hash Sharding 策略的系统如:Cassandra 的一致性 Hash,Redis Cluster 和 Codis 的 Pre-sharding 策略,Twemproxy 有采用一致性 Hash 的配置。


当然这两种策略并不是孤立的,可以灵活组合,比如可以建立多级的 Sharding 策略,最上层用 Hash ,每一个 Hash Sharding 中,数据有序的存储。


在做动态扩展的时候,对于 Range 模型的系统会稍微好做一些,简单来说是采用分裂,比如原本我有一个 [1, 100) 的 Range Region,现在我要分裂,逻辑上我只需要简单的将这个 region 选取某个分裂点,如分裂成 [1,50), [50, 100) 即可,然后将这两个 Region 移动到不同的机器上,负载就可以均摊开。


但是对于 Hash 的方案来说,做一次 re-hash 的代价是挺高的,原因也是显而易见,比如现在的系统有三个节点,现在我添加一个新的物理节点,此时我的 hash 模的 n 就会从 3 变成 4,对于已有系统的抖动是很大,尽管可以通过 ketama hash 这样的一致性 hash 算法尽量的降低对已有系统的抖动,但是很难彻底的避免。


Sharding 与高可用方案结合


选择好了 sharding 的策略,那剩下的就是和高可用方案结合,不同的复制方案达到的可用性及一致性级别是不同的。很多中间件只是简单的做了 sharding 的策略,但是并没有规定每个分片上的数据的复制方案,比如 redis 中间件 twemproxy 和 codis,MySQL 中间件 cobar 等,只是在中间层进行路由,并未假设底层各个存储节点上的复制方案。但是,在一个大规模存储系统上,这是一个很重要的事情,由于支持弹性伸缩的系统一般来说整个系统的分片数量,数据分片的具体分布都是不固定的,系统会根据负载和容量进行自动均衡和扩展,人工手动维护主从关系,数据故障恢复等操作在数据量及分片数量巨大的情况下几乎是不可能完成的任务。选择一个高度自动化的高可用方案是非常重要的。


在 TiKV 中,我们选择了按 range 的 sharding 策略,每一个 range 分片我们称之为 region,因为我们需要对 scan 的支持,而且存储的数据基本是有关系表结构的,我们希望同一个表的数据尽量的在一起。另外在 TiKV 中每一个 region 采用 Raft 算法在多个物理节点上保证数据的一致性和高可用。


图4.png-46kB


从社区的多个 Raft 实现来看,比如 Etcd / LogCabin / Consul 基本都是单一 raft group 的实现,并不能用于存储海量的数据,所以他们主要的应用场景是配置管理,很难直接用来存储大量的数据,毕竟单个 raft group 的参与节点越多,性能越差,但是如果不能横向的添加物理节点的话,整个系统没有办法 scale。


scale 的办法说来也很简单,采用多 raft group,这就很自然的和上面所说的 sharding 策略结合起来了,也就是每一个分片作为一个 raft group,这是 TiKV 能够存储海量数据的基础。但是管理动态分裂的多 raft group 的复杂程度比单 group 要复杂得多,目前 TiKV 是我已知的开源项目中实现 multiple raft group 的仅有的两个项目之一。


正如之前提到过的我们采用的是按照 key range 划分的 region,当某一个 region 变得过大的时候(目前是 64M),这个 region 就会分裂成两个新的 region,这里的分裂会发生在这个 region 所处的所有物理节点上,新产生的 region 会组成新的 raft group。


总结


构建一个健壮的分布式系统是一个很复杂的工程,上面提到了在 TiKV 在实践中的一些关键的设计和思想,希望能抛砖引玉。因为 TiKV 也是一个开源的实现,作为 TiDB 的核心存储组件,最近也刚发布了 Beta 版本,代码面前没有秘密,有兴趣深入了解的同学也可以直接阅读源码和我们的文档,谢谢大家。


Q&A


Q1:如何在这个 region 的各个副本上保证分裂这个操作安全的被执行?


其实这个问题比较简单,就是将 split region 这个操作作为一个 raft log,走一遍 raft 状态机,当这个 log 成功 apply 的时候,即可以认为这个操作被安全的复制了(因为 raft 算法干得就是这个事情)。确保 split log 操作被 accept 后,对新的 region 在走一次 raft 的选举流程(也可以沿用原来的 leader,新 region 的其他节点直接发心跳)。split 的过程是加上网络隔离,可能会产生很复杂的 case,比如一个复杂的例子:


a, b 两个节点,a 是 leader, 发起一个分裂 region 1 [a, d) -> region 1 [a, b) + region 2 [b, d), region 2的 heartbeart 先发到 b,但这时候 region 2 分裂成了 region 2 [b, c) + region 3 [c, d),给 b 发送的 snapshot 是最新的 region 2 的 snapshot [b, c),region 1的 split log 到了 b,b 的老 region 1 也分裂成了 region 1 [a, b) + region 2 [b,d), 这之后 a 给 b 发送的最新的 region 2 的 snapshot [b, c) 到了,region 2 被 apply 之后,b 节点的 region 2 必须没有 [c, d) 区间的数据。


Q2:如何做到透明?


在这方面,raft 做得比 paxos 好,raft 很清晰的提供了 configuration change 的流程,configuration change 流程用于应对 raft gourp 安全的动态添加节点和移除节点,有了这个算法,在数据库中 rebalance 的流程其实能很好的总结为:


对一个 region: add replica / transfer leadership / remove local replica


这三个流程都是标准的 raft 的 configuration change 的流程,TiKV 的实现和 raft 的 paper 的实现有点不一样的是:


config change 的 log 被 apply 后,才会发起 config change 操作,一次一个 group 只能处理一个 config change 操作,避免 disjoint majority,不过这点在 diego 的论文里提到过。


主要是出于正确性没问题的情况下,工程实现比较简单的考虑。
另外这几个过程要做到业务层透明,也需要客户端及元信息管理模块的配合。毕竟当一个 region 的 leader 被转移走后,客户端对这个 region 的读写请求要发到新的 leader 节点上。


客户端这里指的是 TiKV 的 client sdk,下面简称 client , client 对数据的读写流程是这样的:首先 client 会本地缓存一份数据的路由表,这个路由表形如:


{startKey1, endKey1}   ->     {Region1, NodeA}
{startKey2, endKey2} -> {Region2, NodeB}
{startKey3, endKey3} -> {Region3, NodeC}

client 根据用户访问的 key,查到这个 key 属于哪个区间,这个区间是哪个 region,leader 现在在哪个物理节点上,然后客户端查到后直接将这个请求发到这个具体的 node 上,刚才说过了,此时 leader 可能已经被 transfer 到了其他节点,此时客户端会收到一个 region stale 的错误,客户端会向元信息管理服务请求然后更新自己的路由表缓存。


这里可以看到,路由表是一个很重要的模块,它需要存储所有的 region 分布的信息,同时还必须准确,另外这个模块需要高可用。另一方面,刚才提到的数据 rebalance 工作,需要有一个拥有全局视角的调度器,这个调度器需要知道哪个 node 容量不够了,哪个 node 的压力比较大,哪个 node region leader 比较多?以动态的调整 regions 在各个 node 中的分布,因为每个 node 是几乎无状态的,它们无法自主的完成数据迁移工作,需要依靠这个调度器发起数据迁移的操作(raft config change)。


大家应该也注意到了,这个调度器的角色很自然的能和路由表融合成一个模块,在 Google Spanner 的论文中,这个模块的名字叫 Placement Driver, 我们在 TiKV 中沿用了这个名称,简称 pd,pd 主要的工作就是上面提到的两项:1. 路由表 2. 调度器。
Spanner 的论文中并没有过多的介绍 pd 的设计,但是设计一个大规模的分布式存储系统的一个核心思想是一定要假设任何模块都是会 crash 的,模块之间互相持有状态是一件很危险的事情,因为一旦 crash,standby 要立刻启动起来,但是这个新实例状态不一定和之前 crash 的实例一致,这时候就要小心会不会引发问题.
比如一个简单的 case :因为 pd 的路由表是存储在 etcd 上的,但是 region 的分裂是由 node 自行决定的 ( node 才能第一时间知道自己的某个 region 大小是不是超过阈值),这个 split 事件如果主动的从 node push 到 pd ,如果 pd 接收到这个事件,但是在持久化到 etcd 前宕机,新启动的 pd 并不知道这个 event 的存在,路由表的信息就可能错误。


我们的做法是将 pd 设计成彻底无状态的,只有彻底无状态才能避免各种因为无法持久化状态引发的问题。


每个 node 会定期的将自己机器上的 region 信息通过心跳发送给 pd, pd 通过各个 node 通过心跳传上来的 region 信息建立一个全局的路由表。这样即使 pd 挂掉,新的 pd 启动起来后,只需要等待几个心跳时间,就又可以拥有全局的路由信息,另外 etcd 可以作为缓存加速这一过程,也就是新的 pd 启动后,先从 etcd 上拉取一遍路由信息,然后等待几个心跳,就可以对外提供服务。


但是这里有一个问题,细心的朋友也可能注意到了,如果集群出现局部分区,可能某些 node 的信息是错误的,比如一些 region 在分区之后重新发起了选举和分裂,但是被隔离的另外一批 node 还将老的信息通过心跳传递给 pd,可能对于某个 region 两个 node 都说自己是 leader 到底该信谁的?


在这里,TiKV 使用了一个 epoch 的机制,用两个逻辑时钟来标记,一个是 raft 的 config change version,另一个是 region version,每次 config change 都会自增 config version,每次 region change(比如split、merge)都会更新 region version. pd 比较的 epoch 的策略是取这两个的最大值,先比较 region version, 如果 region version 相等则比较 config version 拥有更大 version 的节点,一定拥有更新的信息。


原文链接

开源数据库的现状

文章分享qiuyesuifeng 发表了文章 • 0 个评论 • 539 次浏览 • 2016-10-11 14:57 • 来自相关话题

数据库作为业务的核心,是整个基础软件栈非常重要的一环。近几年的开源社区,新的思想和方案层出不穷,我将总结一下近几年一些主流的开源数据库方案,及其背后的设计思想以及适用场景。本人才疏学浅如有遗漏或者错误请见谅。本次分享聚焦于数据库即结构化数据存储 OLTP... 查看全部

数据库作为业务的核心,是整个基础软件栈非常重要的一环。近几年的开源社区,新的思想和方案层出不穷,我将总结一下近几年一些主流的开源数据库方案,及其背后的设计思想以及适用场景。本人才疏学浅如有遗漏或者错误请见谅。本次分享聚焦于数据库即结构化数据存储 OLTP 及 NoSQL 领域,不会涉及 OLAP、对象存储以及分布式文件系统。


开源 RDBMS 与互联网的崛起
很长时间以来,关系型数据库一直是大公司的专利,市场被 Oracle / DB2 等企业数据库牢牢把持。但是随着互联网的崛起和开源社区的发展,上世纪九十年代 MySQL 1.0 的发布,标志着在关系型数据库的领域,社区终于有了可选择的方案。


✦MySQL


第一个介绍的单机 RDBMS 就是 MySQL。相信大多数朋友都已经对 MySQL 非常熟悉,基本上 MySQL 的成长史就是互联网的成长史。我接触的第一个 MySQL 版本是 MySQL 4.0,后来的 MySQL 5.5 更是经典——基本上所有的互联网公司都在使用。MySQL 也普及了「可插拔」引擎这一概念,即针对不同的业务场景选用不同的存储引擎,这也是 MySQL tuning 的一个重要方式。比如对于有事务需求的场景使用 InnoDB;对于并发读取的场景 MyISAM 可能比较合适;但是现在我推荐绝大多数情况还是使用 InnoDB,毕竟 MySQL 5.6 后它已经成为了官方的默认引擎。MySQL 适用于几乎所有需要持久化结构化数据的场景, 大多数朋友应该都知道,我就不赘述了。


另外值得一提的是 MySQL 5.6 中引入了多线程复制和 GTID,使得故障恢复和主从的运维变得比较方便。另外,MySQL 5.7(目前处于 GA 版本) 发布了一个重大更新,主要是在读写性能和复制性能上有了长足的进步:在 5.6 版本中实现了 SCHEMA 级别的并行复制。不过意义不大,倒是 MariaDB 的多线程并行复制大放异彩,有不少人因为这个特性选择 MariaDB。另外,MySQL 5.7 MTS 支持两种模式,一种是和 5.6 一样,另一种则是基于 binlog group commit 实现的多线程复制,也就是 MASTER 上同时提交的 binlog 在 SLAVE 端也可以同时被 apply,实现并行复制。如果有单机数据库技术选型的朋友,基本上只需要考虑 MySQL 5.7 或者 MariaDB 就好了,而且 MySQL 5.6 和 5.7 由 Oracle 接手后,性能和稳定性都有了明显的提升。


✦PostgreSQL


PostgreSQL 的历史也非常悠久,其前身是 UCB 的 Ingres,主持这个项目的 Michael Stronebraker 于 2015 年获得图灵奖。后来该项目更名为 Post-Ingres,基于 BSD license 下开源。 1995 年几个 UCB 的学生为 Post-Ingres 开发了 SQL 的接口,正式发布了 PostgreSQL95,随后一步步在开源社区中成长起来。和 MySQL 一样,PostgreSQL 也是一个单机的关系型数据库,但是与 MySQL 方便用户过度扩展的 SQL 文法不一样的是,PostgreSQL 的 SQL 支持非常强大,不管是内置类型、JSON 支持、GIS 类型以及对于复杂查询的支持,PL/SQL 等都比 MySQL 强大得多,而且从代码质量上来看,PostgreSQL 的代码质量是优于 MySQL 的。另外,相对于 MySQL 5.7 以前的版本, PostgreSQL 的 SQL 优化器比 MySQL 强大很多,几乎所有稍微复杂的查询 PostgreSQL 的表现都优于 MySQL。


从近几年的趋势上来看,PostgreSQL 的势头也很强劲,我认为 PostgreSQL 的不足之处在于没有 MySQL 那样强大的社区和群众基础。MySQL 经过那么多年的发展,积累了很多的运维工具和最佳实践,但是 PostgreSQL 作为后起之秀,拥有更优秀的设计和更丰富的功能。PostgreSQL 9 以后的版本也足够稳定,在做新项目技术选型的时候,是一个很好的选择。另外也有很多新的数据库项目是基于 PostgreSQL 源码的基础上进行二次开发,比如 Greenplum 等。


我认为,单机数据库的时代很快就会过去。摩尔定律带来的硬件红利总是有上限的,现代业务的数据规模、流量以及现代的数据科学对于数据库的要求,单机已经很难满足。比如,网卡磁盘 IO 和 CPU 总有瓶颈,线上敏感的业务系统可能还得承担 SPOF(单点故障) 的风险,主从复制模型在主挂掉时到底切还是不切?切了以后数据如何恢复?如果只是出现主从机器网络分区问题呢?甚至是监控环境出现网络分区问题呢?这些都是单机数据库面临的巨大挑战。所以我的观点是,无论单机性能多棒(很多令人乍舌的评测数据都是针对特定场景的优化,另外甚至有些都是本机不走网络,而大多数情况数据库出现的第一个瓶颈其实是网卡和并发连接……),随着互联网的蓬勃发展和移动互联网的出现,数据库系统迎来了第一次分布式的洗礼。


分布式时代:NoSQL 的复兴和模型简化的力量
在介绍 NoSQL 之前,我想提两个公司,一个是 Google,另一个是 Amazon。


✦Google


Google 应该是第一个将分布式存储技术应用到大规模生产环境的公司,同时也是在分布式系统上积累最深的公司,可以说目前工业界的分布式系统的工程实践及思想大都来源于 Google。比如 2003 年的 GFS 开创了分布式文件系统,2006 年的 Bigtable 论文开创了分布式键值系统,直接催生的就是 Hadoop 的生态;至于 2012 年发表论文的 Spanner 和 F1 更是一个指明未来关系型数据库发展方向的里程碑式的项目,这个我们后续会说。


✦Amazon


另一个公司是 Amazon。2007 年发表的 Dynamo 的论文 尝试引入了最终一致性的概念, WRN 的模型及向量时钟的应用,同时将一致性 HASH、merkle tree 等当时一些很新潮的技术整合起来,正式标志着 NoSQL 的诞生。NoSQL——对后来业界的影响非常也是很大,包括后来的 Cassandra、RiakDB、Voldemort 等数据库都是基于 Dynamo 的设计发展起来的。


✦新思潮


另外这个时期(2006 年前后持续至今)一个比较重要的思潮就是数据库(持久化)和缓存开始有明确的分离——我觉得这个趋势是从 memcached 开始的。随着业务的并发越来越高,对于低延迟的要求也越来越高;另外一个原因是随着内存越来越便宜,基于内存的存储方案渐渐开始普及。当然内存缓存方案也经历了一个从单机到分布式的过程,但是这个过程相比关系型数据库的进化要快得多。这是因为 NoSQL 的另外一个重要的标志——数据模型的变化——大多 NoSQL 都抛弃了关系模型,选择更简单的键值或者文档类型进行存储。数据结构和查询接口都相对简单,没有了 SQL 的包袱,实现的难度会降低很多。另外 NoSQL 的设计几乎都选择牺牲掉复杂 SQL 的支持及 ACID 事务换取弹性扩展能力,也是从当时互联网的实际情况出发:业务模型简单、爆发性增长带来的海量并发及数据总量爆炸、历史包袱小、工程师强悍,等等。其中最重要的还是业务模型相对简单。


✦嵌入式存储引擎


在开始介绍具体的开源的完整方案前,我想介绍一下嵌入式存储引擎们。


随着 NoSQL 的发展,不仅仅缓存和持久化存储开始细分,再往后的存储引擎也开始分化并走上前台。之前很难想象一个存储引擎独立于数据库直接对外提供服务,就像你不会直接拿着 InnoDB 或者 MyISAM 甚至一个 B-tree 出来用一样(当然,bdb 这样鼎鼎大名的除外)。人们基于这些开源的存储引擎进行进一步的封装,比如加上网络协议层、加上复制机制等等,一步步构建出完整的风格各异的 NoSQL 产品。


这里我挑选几个比较著名的存储引擎介绍一下。


✦TC


我最早接触的是 Tokyo Cabinet(TC)。TC 相信很多人也都听说过,TC 是由日本最大的社交网站 Mixi 开发并开源的一个混合 Key-Value 存储引擎,其中包括 HASH Table 和 B+ Tree 的实现。但是这个引擎的一个缺陷是随着数据量的膨胀,性能的下降会非常明显,而且现在也基本不怎么维护了,所以入坑请慎重。与于 TC 配合使用的 Tokyo Tyrant(TT) 是一个网络库,为 TC 提供网络的接口使其变成一个数据库服务,TT + TC 应该是比较早的 NoSQL 的一个尝试。


✦LevelDB


在 2011 年,Google 开源了 Bigtable 的底层存储引擎: LevelDB。LevelDB 是一个使用 C++ 开发的嵌入式的 Key-Value 存储引擎,数据结构采用了 LSM-Tree,具体 LSM-Tree 的算法分析可以很容易在网上搜索到,我就不赘述了。其特点是,对于写入极其友好,LSM 的设计避免了大量的随机写入;对于特定的读也能达到不错的性能(热数据在内存中);另外 LSM-Tree 和 B-tree 一样是支持有序 Scan 的;而且 LevelDB 是出自 Jeff Dean 之手,他的事迹做分布式系统的朋友一定都知道,不知道的可以去 Google 搜一下。


LevelDB 拥有极好的写性能,线程安全,BatcTCh Write 和 Snapshot 等特性,使其很容易的在上层构建 MVCC 系统或者事务模型,这对于数据库来说非常重要。另外值得一说的是,Facebook 维护了一个活跃的 LevelDB 的分支,名为 RocksDB。RocksDB 在 LevelDB 上做了很多的改进,比如多线程 Compactor、分层自定义压缩、多 MemTable 等。另外 RocksDB 对外暴露了很多 ConfigurationConfigration ,可以根据不同业务的形态进行调优;同时 Facebook 在内部正在用 RocksDB 来实现一个全新的 MySQL 存储引擎:MyRocks,值得关注。RocksDB 的社区响应速度很快也很友好,实际上 PingCAP 也是 RocksDB 的社区贡献者。我建议新的项目如果在 LevelDB 和 RocksDB 之间纠结的话,请果断选择 RocksDB。


✦B-tree 家族


当然,除了 LSM-Tree 外,B-tree 的家族也还是有很多不错的引擎。首先大多数传统的单机数据库的存储引擎都选择了 B+Tree,B+Tree 对磁盘的读比较友好,第三方存储引擎比较著名的纯 B+Tree 实现是 LMDB。首先 LMDB 选择在内存映像文件 (mmap) 实现 B+Tree,而且同时使用了 Copy-On-Write 实现了 MVCC 实现并发事务无锁读的能力,对于高并发读的场景比较友好;同时因为使用的是 mmap 所以拥有跨进程读取的能力。不过因为我并没有在生产环境中使用过 LMDB ,所以并不能给出 LMDB 的一些缺陷,见谅。


✦混合引擎


还有一部分的存储引擎选择了多种引擎混合,比如最著名的应该是 WiredTiger,大概是 2014 年去年被 MongoDB 收购,现在成为了 MongoDB 的默认存储引擎。WiredTiger 内部有 LSM-Tree 和 B-tree 两种实现,对外提供相同的一套接口,根据业务的情况可自由选择。另外一些特殊数据结构的存储引擎在某些特殊场合下非常抢眼,比如极高压缩比 TokuDB,采用了名为分形树的数据结构,在维持一个可接受的读写压力的情况下,能拥有 10 倍以上的压缩率。


✦NoSQL


说完了几个比较著名的存储引擎,我们来讲讲比较著名的 NoSQL。在我的定义中,NoSQL 是 Not Only SQL 的缩写,所以可能包含的范围有内存数据库,持久化数据库等。总之就是和单机的关系型数据库不一样的结构化数据存储系统。


我们先从缓存开始。


✦memcached


前面提到了 memcached 应该是第一个大规模在业界使用的缓存数据库,memcached 的实现极其简单,相当于将内存用作大的 HASH Table,只能在上面进行 get/set/ 计数器等操作,在此之上用 libevent 封装了一层网络层和文本协议(也有简单的二进制协议),虽然支持一些 CAS 的操作,但是总体上来看,还是非常简单的。但是 memcached 的内存利用率并不太高,这是这个因为 memcached 为了避免频繁申请内存导致的内存碎片的问题,采用了自己实现的 slab allocator 的方式。即内存的分配都是一块一块的,最终存储在固定长度的 chunk 上,内存最小的分配单元是 chunk,另外 libevent 的性能也并没有优化到极致。但是这些缺点并不妨碍 memcached 成为当时的开源缓存事实标准。(另外,八卦一下,memcached 的作者 Brad Fitzpatrick 现在在 Google,大家如果用 Golang 的话,Go 的官方 HTTP 包就是这哥们写的,是个很高产的工程师)。


✦Redis


如果我没记错的话,在 2009 年前后,一位意大利的工程师 Antirez ,开源了 Redis。从此彻底颠覆了缓存的市场,到现在大多数缓存的业务都已用上 Redis,memcached 基本退出了历史舞台。Redis 最大的特点是拥有丰富的数据结构支持,不仅仅是简单的 Key-Value,还包括队列、集合、Sorted Set 等等,提供了非常丰富的表达力,而且 Redis 还提供 sub/pub 等超出数据库范畴的便捷功能,使得几乎一夜之间大家纷纷投入 Redis 的怀抱。


✦Twemproxy


但是随着 Redis 渐渐的普及,而且越用越狠,另外内存也越来越便宜,人们开始寻求扩展单机 Redis 的方案,最早的尝试是 twitter 开源的 twemproxy。twemproxy 是一个 Redis 中间件,基本只有最简单的数据路由功能,并没有动态的伸缩能力,但是还是受到了很多公司的追捧,因为确实没其他替代方案。随后的 Redis Cluster 也是难产了好久,时隔好几年,中间出了 7 个 RC 版本,最后才发布;2014 年底,我们开源了 Codis,解决了 Redis 中间件的数据弹性伸缩问题,目前广泛应用于国内各大互联网公司中,这个在网上也有很多文章介绍,我也就不展开了。所以在缓存上面,开源社区现在倒是非常统一,就是 Redis 及其极其周边的扩展方案。


✦MongoDB


在 NoSQL 的大家庭中,MongoDB 其实是一个异类,大多 NoSQL 舍弃掉 SQL 是为了追求更极致的性能和可扩展能力,而 MongoDB 主动选择了文档作为对外的接口,非常像 JSON 的格式。Schema-less 的特性对于很多轻量级业务和快速变更的了互联网业务意义很大,而且 MongoDB 的易用性很好,基本做到了开箱即用,开发者不需要费心研究数据的表结构,只需要往里存就好了,这确实笼络了一大批开发者。


尽管 MongoDB 早期的版本各种不稳定,性能也不太好(早期的 Mongo 并没有存储引擎,直接使用了 mmap 文件),集群模式还全是问题(比如至今还未解决的 Cluster 同步带宽占用过多的问题),但是因为确实太方便了,在早期的项目快速迭代中,Mongo 是一个不错的选择。但是这也正是它的问题,我不止一次听到当项目变得庞大或者「严肃」的时候,团队最后还是回归了关系型数据库。Anyway,在 2014 年底 MongoDB 收购了 WiredTiger 后,在 2.8 版本中正式亮相,同时 3.0 版本后更是作为默认存储引擎提供,性能和稳定性有了非常大的提升。


但是,从另一方面讲,Schema-less 到底对软件工程是好事还是坏事这个问题还是有待商榷。我个人是站在 Schema 这边的,不过在一些小项目或者需要快速开发的项目中使用 Mongo 确实能提升很多的开发效率,这是毋庸置疑的。


✦HBase


说到 NoSQL 不得不提的是 HBase,HBase 作为 Hadoop 旗下的重要产品,Google Bigtable 的正统开源实现,是不是有一种钦定的感觉 :)。提到 HBase 就不得不提一下 Bigtable, Bigtable 是 Google 内部广泛使用的分布式数据库,接口也不是简单的 Key-Value,按照论文的说法叫:multi-dimensional sorted map,也就是 Value 是按照列划分的。Bigtable 构建在 GFS 之上,弥补了分布式文件系统对于海量、小的、结构化数据的插入、更新以及、随机读请求的缺陷。


HBase 就是这么一个系统的实现,底层依赖 HDFS。HBase 本身并不实际存储数据,持久化的日志和 SST file (HBase 也是 LSM-Tree 的结构) 直接存储在 HDFS 上,Region Server (RS) 维护了 MemTable 以提供快速的查询,写入都是写日志,后台进行 Compact,避免了直接随机读写 HDFS。数据通过 Region 在逻辑上进行分割,负载均衡通过调节各个 Region Server 负责的 Region 区间实现。当某 Region 太大时,这个 Region 会分裂,后续可能由不同的 RS 负责,但是前面提到了,HBase 本身并不存储数据,这里的 Region 仅是逻辑上的,数据还是以文件的形式存储在 HDFS 上,所以 HBase 并不关心 Replication 、水平扩展和数据的分布,统统交给 HDFS 解决。


和 Bigtable 一样,HBase 提供行级的一致性,严格来说在 CAP 理论中它是一个 CP 的系统,但遗憾的是并没有更进一步提供 ACID 的跨行事务。HBase 的好处就不用说了,显而易见,通过扩展 RS 可以几乎线性提升系统的吞吐,及 HDFS 本身就具有的水平扩展能力。


但是缺点仍然是有的。首先,Hadoop 的软件栈是 Java,JVM 的 GC Tuning 是一个非常烦人的事情,即使已经调得很好了,平均延迟也得几十毫秒;另外在架构设计上,HBase 本身并不存储数据,所以可能造成客户端请求的 RS 并不知道数据到底存在哪台 HDFS DataNode 上,凭空多了一次 RPC;第三,HBase 和 Bigtable 一样,并不支持跨行事务,在 Google 内部不停的有团队基于 Bigtable 来做分布式事务的支持,比如 MegaStore、Percolator。后来 Jeff Dean 有次接受采访也提到非常后悔没有在 Bigtable 中加入跨行事务,不过还好这个遗憾在 Spanner 中得到了弥补,这个一会儿说。总体来说,HBase 还是一个非常健壮且久经考验的系统,但是需要你有对于 Java 和 Hadoop 比较深入的了解后,才能玩转,这也是 Hadoop 生态的一个问题,易用性真是不是太好,而且社区演进速度相对缓慢,也是因为历史包袱过重的缘故吧。


✦Cassandra


提到 Cassandra (C),虽然也是 Dynamo 的开源实现,但就没有这种钦定的感觉了。C 确实命途多舛,最早 2008 由 Facebook 开发并开源,早期的 C* 几乎全是 bug,Facebook 后来索性也不再维护转过头搞 HBase 去了,一个烂摊子直接丢给社区。还好 DataStax 把这个项目捡起来商业化,搞了两年,终于渐渐开始流行起来。


C 不能简单的归纳为读快写慢,或者读慢写快,因为采用了 qourm 的模型,调整复制的副本数以及读的数量,可以达到不同的效果,对于一致性不是特别高的场景,可以选择只从一个节点读取数据,达到最高的读性能。另外 C 并不依赖分布式文件系统,数据直接存储在磁盘上,各个存储节点之间自己维护复制关系,减少了一层 RPC 调用,延迟上对比 HBase 还是有一定优势的。


不过即使使用 qourm 的模型也并不代表 C 是一个强一致的系统。C 并不帮你解决冲突,即使你 W(写的副本数) + R(读请求的副本数) > N(节点总数),C 也没办法帮你决定哪些副本拥有更新的版本,因为每个数据的版本是一个 NTP 的时间戳或者客户端自行提供,每台机器可能都有误差,所以有可能并不准确,这也就是为什么 C 是一个 AP 的系统。不过 C* 一个比较友好的地方是提供了 CQL,一个简单的 SQL 方言,比起 HBase 在易用性上有明显优势。


即使作为一个 AP 系统,C 已经挺快了,但是人们追求更高性能的脚步还是不会停止。应该是今年年初,ScyllaDB 的发布就是典型的证明,ScyllaDB 是一个兼容 C 的 NoSQL 数据库,不一样的是,ScyllaDB 完全用 C++ 开发,同时使用了类似 DPDK 这样的黑科技,具体我就不展开了,有兴趣可以到 Scylla 的官网去看看。BTW, 国内的蘑菇街第一时间使用了 ScyllaDB,同时在 Scylla 的官网上 share 了他们的方案,性能还是很不错的。


中间件与分库分表
NoSQL 就先介绍到这里,接下来我想说的是一些在基于单机关系型数据库之上的中间件和分库分表方案。


这些技术确实历史悠久,而且也是没有办法的选择。关系型数据库不比 Redis,并不是简单的写一个类似 Twemproxy 的中间件就搞定了。数据库的中间件需要考虑很多,比如解析 SQL,解析出 sharding key,然后根据 sharding key 分发请求,再合并;另外数据库有事务,在中间件这层还需要维护 Session 及事务状态,而且大多数方案并没有办法支持跨 shard 的事务。这就不可避免的导致了业务使用起来会比较麻烦,需要重写代码,而且会增加逻辑的复杂度,更别提动态的扩容缩容和自动的故障恢复了。在集群规模越来越大的情况下,运维和 DDL 的复杂度是指数级上升的。


✦中间件项目盘点


数据库中间件最早的项目大概是 MySQL Proxy,用于实现读写分离。后来国人在这个领域有过很多著名项目,比如阿里的 Cobar 和 TDDL(并未完全开源);后来社区基于 Cobar 改进的 MyCAT、360 开源的 Atlas 等,都属于这一类中间件产品;在中间件这个方案上基本走到头的开源项目应该是 Youtube 的 Vitess。Vitess 基本上是一个集大成的中间件产品,内置了热数据缓存、水平动态分片、读写分离等等,但是代价也是整个项目非常复杂,另外文档也不太好。大概 1 年多以前,我们尝试搭建起完整的 Vitess 集群,但是并未成功,可见其复杂度。


另外一个值得一提的是 Postgres-XC 这个项目,Postgres-XC 的野心还是很大的,整体的架构有点像早期版本的 OceanBase,由一个中央节点来处理协调分布式事务 / 解决冲突,数据分散在各个存储节点上,应该是目前 PostgreSQL 社区最好的分布式扩展方案。其他的就不提了。


未来在哪里?NewSQL!
一句话,NewSQL 就是未来。


2012 年 Google 在 OSDI 上发表了 Spanner 的论文,2013 年在 SIGMOD 发表了 F1 的论文。这两篇论文让业界第一次看到了关系模型和 NoSQL 的扩展性在超庞大集群规模上融合的可能性。在此之前,大家普遍认为这个是不可能的,即使是 Google 也经历了 Megastore 这样的失败。


✦Spanner 综述


但是 Spanner 的创新之处在于通过硬件(GPS 时钟 + 原子钟)来解决时钟同步的问题。在分布式系统里,时钟是最让人头痛的问题,刚才提到了 C* 为什么不是一个强 C 的系统,正是因为时钟的问题。而 Spanner 的厉害之处在于即使两个数据中心隔得非常远,不需要有通信(因为通信的代价太大,最快也就是光速)就能保证 TrueTime API 的时钟误差在一个很小的范围内(10ms)。另外 Spanner 沿用了很多 Bigtable 的设计,比如 Tablet / Directory 等,同时在 Replica 这层使用 Paxos 复制,并未完全依赖底层的分布式文件系统。但是 Spanner 的设计底层仍然沿用了 Colossus,不过论文里也说是可以未来改进的点。


Google 的内部的数据库存储业务,大多是 3~5 副本,重要一点的 7 副本,遍布全球各大洲的数据中心,由于普遍使用了 Paxos,延迟是可以缩短到一个可以接受的范围(Google 的风格一向是追求吞吐的水平扩展而不是低延迟,从悲观锁的选择也能看得出来,因为跨数据中心复制是必选的,延迟不可能低,对于低延迟的场景,业务层自己解决或者依赖缓存)。另外由 Paxos 带来的 Auto-Failover 能力,更是能让整个集群即使数据中心瘫痪,业务层都是透明无感知的。另外 F1 构建在 Spanner 之上,对外提供了更丰富的 SQL 语法支持,F1 更像一个分布式 MPP SQL——F1 本身并不存储数据,而是将客户端的 SQL 翻译成类似 MapReduce 的任务,调用 Spanner 来完成请求。


其实 Spanner 和 F1 除了 TrueTime 整个系统并没有用什么全新的算法,其意义在于这是近些年来第一个 NewSQL 在生产环境中提供服务的分布式系统技术。


Spanner 和 F1 有以下几个重点:



  1. 完整的 SQL 支持,ACID 事务;

  2. 弹性伸缩能力;

  3. 自动的故障转移和故障恢复,多机房异地灾备。


NewSQL 特性确实非常诱人,在 Google 内部,大量的业务已经从原来的 Bigtable 切换到 Spanner 之上。我相信未来几年,整个业界的趋势也是如此,就像当年的 Hadoop 一样,Google 的基础软件的技术趋势是走在社区前面的。


✦社区反应


Spanner 的论文发表之后,当然也有社区的追随者开始实现(比如我们 :D ),第一个团队是在纽约的 CockroachDB。CockroachDB 的团队的组成还是非常豪华的,早期团队由是 Google 的分布式文件系统 Colossus 团队的成员组成;技术上来说,Cockroach 的设计和 Spanner 很像,不一样的地方是没有选择 TrueTime 而是 HLC (Hybrid logical clock),也就是 NTP + 逻辑时钟来代替 TrueTime 时间戳;另外 Cockroach 选用了 Raft 代替 Paxos 实现复制和自动容灾,底层存储依赖 RocksDB 实现,整个项目使用 Go 语言开发,对外接口选用 PostgreSQL 的 SQL 子集。


✦TiDB


目前从全球范围来看,另一个朝着 Spanner / F1 的开源实现这个目标上走的产品是 TiDB(终于谈到我们的产品了)。TiDB 本质上是一个更加正统的 Spanner 和 F1 实现,并不像 CockroachDB 那样选择将 SQL 和 Key-Value 融合,而是像 Spanner 和 F1 一样选择分离,这样分层的思想也是贯穿整个 TiDB 项目始终的。对于测试、滚动升级以及各层的复杂度控制会比较有优势;另外 TiDB 选择了 MySQL 协议和语法的兼容,MySQL 社区的 ORM 框架和运维工具,直接可以应用在 TiDB 上。


和 F1 一样,TiDB 是一个无状态的 MPP SQL Layer,整个系统的底层是依赖 TiKV 来提供分布式存储和分布式事务的支持。TiKV 的分布式事务模型采用的是 Google Percolator 的模型,但是在此之上做了很多优化。Percolator 的优点是去中心化程度非常高,整个集群不需要一个独立的事务管理模块,事务提交状态这些信息其实是均匀分散在系统的各个 Key 的 meta 中,整个模型唯一依赖的是一个授时服务器。在我们的系统上,极限情况这个授时服务器每秒能分配 400w 以上个单调递增的时间戳,大多数情况基本够用了(毕竟有 Google 量级的场景并不多见);同时在 TiKV 中,这个授时服务本身是高可用的,也不存在单点故障的问题。


TiKV 和 CockroachDB 一样也是选择了 Raft 作为整个数据库的基础;不一样的是,TiKV 整体采用 Rust 语言开发,作为一个没有 GC 和 Runtime 的语言,在性能上可以挖掘的潜力会更大。


✦关于未来


我觉得未来的数据库会有几个趋势,也是 TiDB 项目追求的目标:


●数据库会随着业务云化,未来一切的业务都会跑在云端,不管是私有云、公有云还是混合云,运维团队接触的可能再也不是真实的物理机,而是一个个隔离的容器或者「计算资源」。这对数据库也是一个挑战,因为数据库天生就是有状态的,数据总是要存储在物理的磁盘上,而移动数据的代价比移动容器的代价可能大很多。


●多租户技术会成为标配,一个大数据库承载一切的业务,数据在底层打通,上层通过权限,容器等技术进行隔离;但是数据的打通和扩展会变得异常简单,结合第一点提到的云化,业务层可以再也不用关心物理机的容量和拓扑,只需要认为底层是一个无穷大的数据库平台即可,不用再担心单机容量和负载均衡等问题。


●OLAP 和 OLTP 会进一步细分,底层存储也许会共享一套,但是 SQL 优化器这层的实现一定是千差万别的。对于用户而言,如果能使用同一套标准的语法和规则来进行数据的读写和分析,会有更好的体验。


●在未来分布式数据库系统上,主从日志同步这样落后的备份方式会被 Multi-Paxos / Raft 这样更强的分布式一致性算法替代,人工的数据库运维在管理大规模数据库集群时是不可能的,所有的故障恢复和高可用都会是高度自动化的。


Q
&
A
问:HANA 等内存数据库怎么保证系统掉电而处理结果不丢?传统数据库也用缓存,可是 HANA 用的内存太大。


黄东旭:没用过 HANA,但是直观感觉这类内存数据库的可用性可能通过集中方式保证:
●写入会先写 WAL;




  • 写入可能会通过主从或者 paxos 之类的算法做同步和冗余复制还有 HANA 本身就是内存数据库,会尽可能把数据放到内存里,这样查询才能快呀。



问:对于传统创业公司如何弥补 NoSQL 的技术短板?快速的引入 NoSQL 提高效率?


黄东旭:选用 NoSQL 主要注意两点:



  1. 做好业务的调研,估计并发量,数据量,数据的结构看看适不适合;

  2. 对各种 NoSQL 擅长和不擅长的地方都尽可能了解。
    不要盲目相信关系型数据库,也不要盲目相信 NoSQL,没有银弹的。


问:有多个条件 比如年龄 20 到 30 或年龄 35 到 40 并且加入购物车或下单 这种数据怎么存储?


黄东旭:购物车这种场景是典型的 OLTP 的场景,可以选用关系型数据库 MySQL PostgreSQL 什么的,如果对于扩展性的数据跨机房有要求的话,可以调研一下 NewSQL,比如我们的 TiDB。


问:多纬度查询应该选择哪种数据库?


黄东旭:多纬度查询可以说是一个 OLAP 的场景,可以选用 Greenplum 或者 Vertica 之类的分析性数据库。


问:想知道为什么需要这些开源的数据库,既然已经有了 MySQL、DB2、Oracle 这些成熟的数据库,成本考虑,还是传统数据库满足不了需求?


黄东旭:对,传统数据库的扩展性是有问题的,在海量并发和数据量的场景下很难支持业务。所以可以看到比较大的互联网公司基本都有自己的分布式数据库方案。


问:未来可能不再需要数据仓库吗?


黄东旭:大家可以想想数据仓库的定义,如果是还需要离线的从线上库倒腾数据到数据仓库上,这样很难做到实时查询,而且空间的利用率也低,我认为是目前并没有太好的方案的情况下的折衷……
如果有一个更好的数据库能解决数据仓库的场景,为什么还需要一个独立的数据仓库?


原文链接

分布式数据库 TiDB 过去现在和未来

文章分享qiuyesuifeng 发表了文章 • 0 个评论 • 2786 次浏览 • 2016-10-11 14:50 • 来自相关话题

近年来,随着移动互联网、物联网、人工智能等技术的兴起,我们已经进入了一个信息爆炸的大数据时代,需要处理和分析的数据越来越多,与此同时,PC Server 的造价不断降低,促使 Hadoop、Spark 等分布式分析计算框架得到广泛的使用,可以肯定的是,在... 查看全部

近年来,随着移动互联网、物联网、人工智能等技术的兴起,我们已经进入了一个信息爆炸的大数据时代,需要处理和分析的数据越来越多,与此同时,PC Server 的造价不断降低,促使 Hadoop、Spark 等分布式分析计算框架得到广泛的使用,可以肯定的是,在未来,分布式一定是主流的数据处理技术。TiDB 是 PingCAP 团队开发的一款开源的 NewSQL 数据库,整个设计参考 Google Spanner 和 F1,目标是构建一个面向高并发、高吞吐的在线海量数据存储的关系型数据库,提供透明的跨行事务及兼容 MySQL 的 SQL 语法支持,支持无缝的水平伸缩以及跨数据中心多活等特性。目前项目社区相当活跃(5000+ Stars),已经成为在国际上有影响力的顶级开源项目。


为什么要做这么一个数据库?现在的方案有什么问题吗?其实一直以来,在关系型数据库这一层几乎没有一种优雅的分布式方案。但是作为存储架构的核心,关系型数据库的水平扩展是不可避免的,目前大多数方案基本都需要对业务层有很强的侵入性,比如采用 NoSQL 替换原有关系型数据库,但是这样就需要涉及大规模的业务重构,相当于将数据库层的复杂度转嫁给业务层;另外一种方案是通过中间件,或者分库分表,但是这种方案仍然很难做到透明和数据的均匀分布,也无法支持一致性的跨节点事务和 JOIN 操作,而且随着集群规模的增大,维护和扩展的复杂度呈指数级上升。另外经常被人忽略的是,两种方案都没有很好的解决高可用的问题,跨机房多活、故障恢复、扩容经常都需要繁重的人工介入。直到 Google 发布 Spanner 和 F1 ,这才出现了一个真正在 Google 这样的业务规模上验证过的分布式关系型数据库。作为 Google Spanner 和 F1 的开源实现,TiDB 和 TiKV 完美地解决了现有的这些问题。下面简单介绍一下我们是如何解决这些问题的。


TiDB 包含三个子项目:一个是 TiDB,一个是 TiKV,还有 PD (placement driver)。TiDB 是一个无状态的 SQL 层,客户端可以任意连接到一个 TiDB Server 实例上,看到的存储层是完全一致的。TiDB 负责解析用户的 SQL 请求,生成查询计划,交给底层的 TiKV 去执行,TiDB 和 TiKV 通过 Protobuf RPC 进行通信;而 TiKV 负责实际的数据存储,支持全局分布式事务,提供对上层透明的水平扩展。另外一个组件 PD 负责存储元信息,如 Region 具体分布在哪台物理节点上,每个 TiKV Node 每隔一定的时间会将自己的状态信息和本机的 Region 分布情况上报给 PD,PD 会根据需要进行 Region 数据的移动和发起添加副本指令等操作,可以说是集群的大脑,整个 TiDB 项目的架构大致就是如此。
水平扩展
我们先来看看 TiKV 是如何实现无缝的水平扩展的。


从以上 TiKV 的架构图中可以看出,TiKV 将数据逻辑上分为很多小的 Region,每一个 Region 是一系列连续按照字节排序的 Key-value 对。和很多传统的中间件方案不同的是,TiKV 并不关心上层的 Schema 结构,也不知道上层每个 Column 的类型,就是一个很纯粹的 Key-value 数据库。 数据移动的单位是 Region,每个 Region 都会在不同的机器上维护副本(默认 3 副本)。当一个 Region 的数据增长到一定程度的时候,这个 Region 就会像细胞分裂一样会分裂成两个新的 Region,然后新的 Region 可能会被移动到不同的机器上,用这种方式来进行负载均衡以及容量的扩展。


在 TiKV 中,每个 Region 的不同副本之间通过 Raft 协议进行强一致保证,我们在 TiKV 的上层实现了两阶段提交(2PC)来支持跨 Region 的透明分布式事务。整个模型正是 Raft 的作者 Diego Ongaro 博士于 2014 年其博士论文中描述的理想的 Large-scale storagesystem 的模型:


高可用与跨数据中心多活
其实如果抛开数据库层面来谈高可用并不是太复杂,因为业务逻辑服务是比较容易设计成无状态的(一般来说,状态都会持久化到存储层)。现在很多比较前卫的互联网公司,已经在业务的最上层通过动态 DNS + LVS 、F5、HAProxy 等负载均衡工具配合 Docker 以及调度器(K8S 或者 Mesos)实现按需的动态伸缩。如果不考虑存储层,即使整个数据中心宕机,业务层也能做到透明的 Failover,也就是多起几个容器的事情,难点无非是控制好瞬时流量等问题,这从整个模型看是比较完善的。


但是到数据库这层就不那么美好了,传统的关系型数据库的容灾主要依赖主从(master-slave)模型,比如 MySQL 的半同步复制,Oracle 的 DataGuard 都属于主从复制的范畴。但是主从模型有两个很大的问题,即数据的一致性和故障的自动切换。在主从模型中,即使是热备,在极端情况下也并不能保证 Failover 时数据的一致性。网络环境错综复杂,比如出现集群脑裂的状态(网络部分隔离),监控程序自动的将副本提升 master,但是原来的 master 可能仍然在处理部分客户端的请求,这种情况下,数据就可能发生不一致。这也就是为什么很多一致性敏感的业务即使主库挂掉了,宁可暂停服务也不敢自动切换热备,这样业务的中断时间就不可控了。再者,如果面对一个非常庞大的集群,可能上百个物理节点甚至更多,机器故障是常态,如果每一次故障都需要人工介入,整个系统的维护代价是非常惊人的。


那有没有办法能在强一致的前提下做到安全的故障恢复和故障转移呢?答案是有的。Spanner 的做法是利用 Multi-Paxos 进行同步,这类分布式选举算法拥有很强的容错能力,即使发生节点宕机,网络隔离,整个系统都不会发生数据丢失或者不一致的状态。而且在保证安全的同时,所有的 Failover 工作都是自动的。自动化是一个能弹性水平扩展的系统的重要条件,因为一切需要人工介入的工作都是无法弹性水平扩展的。但是长期以来,由于 Paxos 的实现极其复杂,社区并没有可以在生产环境中使用的实现。


在 TiKV 中我们采用了 Raft 算法来作为和 Spanner 中的 Multi-Paxos。Raft 是 Diego Ongaro 在 2014 年发布的一个全新的先进的分布式一致性算法,在正确性和性能上和 Multi-Paxos 等价,但是对工程实现的复杂度控制很好。目前 TiKV 的 Raft 实现是和 CoreOS 一起合作开发的。CoreOS 是知名的 Raft 项目 etcd 背后的公司。抛弃传统的主从模型,而选用 Raft 这样的分布式一致性算法为 TiDB 在稳定性和健壮性带来了质的飞跃。可以说,整个 TiDB 系统都是构建在 Raft 这个基石之上的。


另外,得益于 Raft 算法的特性,TiDB 可以在保证延迟处于可接受范围内的情况下,能够真正支持跨数据中心多活,这也是 TiDB 的一个重要特点。现在很多对于强一致性要求极端严格的业务,比如金融、支付、计费等,两地三中心的部署渐渐成为共识,但是在多数据中心容灾这个事情上很难找到一个很好的方案。在传统的方案中,如果要做到强一致的热备,需要所有数据中心的副本同步写成功,一个写入的延迟取决于所有数据中心尤其是最远的数据中心的延迟。而在 TiDB 上,由于 Raft 协议只需要同步复制到「大多数」的数据中心即可保证数据的强一致性,所以写入的延迟取决于「大多数」数据中心的延迟。一般来说,对于两地三中心的部署,一定会有一个同城双机房,这时候的延迟较于异地机房的延迟就会小很多了。
完整的面向分布式存储的 SQL Layer
TiDB 实现了完整的 SQL 解析器和优化器。传统的中间件方案只做简单的路由和转发到底层的数据库上,并不能很好地支持 JOIN 或者透明的事务,也就很难完整地兼容原有的业务,对业务层有比较大的侵入性。另外传统的单机数据库的查询优化器并没有考虑到底层存储可能是一个分布式存储,很多优化技巧和手段都难以利用分布式的计算能力和优势。出于以上几点原因,TiDB 选择了重新实现完整的 SQL 解析和 MPP SQL 优化器等组件以支持客户端透明的复杂查询,以及分布式事务。
另外 TiDB 支持完整的 MySQL 语法和网络协议兼容。这样,TiDB 可以直接使用 MySQL 社区的海量测试用例,大量的已有 DBA 工具,更使得那些使用 MySQL 的客户的迁移和兼容的工作成本降到最低。这很好的替代了传统的分库分表和中间件等过渡方案。而且 TiDB 还能完美兼容 MySQL 的 binlog, 可以和现有的 MySQL 互为备份,进一步降低了早期的测试成本。
产品规划
分布式执行引擎


TiDB 目前内置一个分布式执行框架,采用和 HBase Coprocessor 类似的方案。这套框架可以利用多台 TiKV 并行处理数据,再将结果汇集到单台 TiDB 来做最终的处理。在处理 OLTP 请求以及中小规模的 OLAP 请求时,这套框架用起来得心应手。但是当处理大型的 OLAP 请求(例如超大表之间的 Join)时,就显得力不从心了。后续我们计划引入一套新的分布式执行引擎,来专门处理复杂 Query。


Spark 集成


Spark 是当前最火的大数据分析工具,在业界得到广泛的应用。TiDB 可以通过 JDBC 接口和 Spark 对接,原有的基于 MySQL 的 Spark 作业可以无缝迁移到 TiDB 上。这样用户可以同时享有 TiDB 海量存储的能力以及 Spark 强大的通用计算能力,从数据中挖掘出更大的价值。这也让 TiDB 同时支持了 OLTP 和 OLAP 两种生态。


Kubernetes


另外一个很重要的事情就是将 TiDB 的整个部署与 Kubernetes 整合。我们认为未来的数据库一定会和云深度结合以提供无缝的扩展及部署体验,而第一步就是和容器调度器的深度整合,我们在内部的测试版本中已经成功的将 TiDB 的部署和扩展在 Kubernetes 上实现,相信很快就会在社区中亮相。


社区情况 / 如何进行贡献


目前 TiDB 项目的社区贡献者达近百人,其中包括京东和华为的大量代码及 Use case 贡献;作为一个国际化开源项目,CoreOS 及 Facebook 等公司也加入进来一起合作开发,因此也非常感谢 Facebook 的 RocksDB team 和 CoreOS 的 etcd team 的大力支持。同时,TiKV 作为 Rust 社区的顶级项目,得到了 Rust 语言官方的大力支持,项目被收录到 Rust Weekly 作为常驻栏目向 Rust 社区通报每周进展。TiDB 作为一个开放的开源项目,欢迎一切感兴趣的开发者的加入,在 Github 的 TiDB 项目首页有 How to contribute 的文档,Welcome aboard and happy hacking!


原文链接

[校招][实习] PingCAP 招前端开发工程师

回复

招聘应聘qiuyesuifeng 发起了问题 • 1 人关注 • 0 个回复 • 986 次浏览 • 2016-10-11 14:30 • 来自相关话题

[校招][实习] PingCAP 招后端开发工程师

回复

招聘应聘qiuyesuifeng 发起了问题 • 1 人关注 • 0 个回复 • 1055 次浏览 • 2016-10-11 14:27 • 来自相关话题

Hello from PingCAP/TiDB

Go开源项目qiuyesuifeng 发表了文章 • 0 个评论 • 356 次浏览 • 2016-10-11 14:17 • 来自相关话题

Hello Gophers,

We're PingCAP, the team behind 查看全部

Hello Gophers,


We're PingCAP, the team behind TiDB. Welcome to follow us on Github!

beego中的重定向

有问必答angelina 回复了问题 • 2 人关注 • 2 个回复 • 1688 次浏览 • 2016-10-11 11:19 • 来自相关话题

golang适合做web开发吗?

有问必答zituocn 回复了问题 • 2 人关注 • 2 个回复 • 1114 次浏览 • 2016-10-12 11:28 • 来自相关话题

提问的智慧

文章分享astaxie 发表了文章 • 1 个评论 • 409 次浏览 • 2016-10-11 10:28 • 来自相关话题

提问的智慧

艾瑞克.史蒂文.雷蒙德(Eric Steven Raymond)

查看全部

提问的智慧


艾瑞克.史蒂文.雷蒙德(Eric Steven Raymond)


https://github.com/ruby-china/How-To-Ask-Questions-The-Smart-Way/blob/master/README-zh_CN.md


这是一篇长文,看完需要十几分钟的时间。如果之前没有认真看过并且思考过,这十几分钟会改变你的职业生涯。这文章可能会出现一些让人不适的词语或者过时的例子,但我认为这不会影响它要表达的内容,而你需要好好琢磨作者的思想。


《提问的智慧》是一个敲门砖,它把黑客间的礼仪和准则明白地写下来,让我们了解到一个事实,为什么那些看起来很牛的人几乎从不提问。其实他们也有问题,但是通常在提问之前就自己解决了。不是因为他们本来就牛,而是解决问题的经历让他们成为牛人。最终,你只会看到网络上多了一篇文章:关于解决XXX问题的方案。


要不要花十几分钟改变自己的人生,决定权在自己。

10.11 每日早报

文章分享astaxie 发表了文章 • 0 个评论 • 453 次浏览 • 2016-10-11 09:52 • 来自相关话题

10.11 每日早报

新闻:

1.哔哩哔哩正式上线VIP会员制度“大会员”,单月价格25元

2.Mozilla为Firefox Sync引入设备管理功能,可远程取消设备同步

3.京东发起公益活动,在... 查看全部

10.11 每日早报


新闻:


1.哔哩哔哩正式上线VIP会员制度“大会员”,单月价格25元


2.Mozilla为Firefox Sync引入设备管理功能,可远程取消设备同步


3.京东发起公益活动,在北京和天津推出快递小哥上门收旧衣服活动


4.花椒直播获3亿元A轮融资,360投资6000万元


5.福田汽车牵手百度,聚焦无人驾驶超级卡车和车联网


6.ofo共享单车完成1.3亿美元C轮融资,宣布向非校园的城市用户开放


7.电子合同签署云平台契约锁获得900万元天使轮融资,上海泛微网络领投


资源:


2016上半年度中国快递市场研究报告
http://www.bigdata-research.cn/content/201610/354.html


注:上述内容来源于互联网,由EGO整理

一个支持负载均衡,健康检查的 TcpProxy

开源程序astaxie 发表了文章 • 1 个评论 • 700 次浏览 • 2016-10-11 07:31 • 来自相关话题

goTcpProxy


GitHub:https://github.com/zheng-ji/goTcpProxy


Go Report Card


一个支持负载均衡,健康检查的 TcpProxy


smailltcp


Description


English



  • A tcp proxy service

  • Supprot multi backend severs

  • Consistent Hash Load Balance

  • Auto detect down server, and remove it.

  • Monitor backend health status


中文



  • TCP 代理服务

  • 后端支持多个服务器

  • 支持一致性哈希的负载均衡

  • 自动检测失败的后端服务器,并移除

  • 后端服务的健康检查接口


How To Compile


cd $GOPATH;
git clone http://github.com/zheng-ji/goTcpProxy;
make

How To Use


配置文件详解


bind: 0.0.0.0:9999      // 代理服务监听端口
wait_queue_len: 100 // 等待队列长度
max_conn: 10000 // 并发最大连接
timeout: 5 // 请求超时时间
failover: 3 // 后端服务允许失败次数
stats: 0.0.0.0:19999 // 健康检查接口
backend: // 后端服务列表
- 127.0.0.1:80
- 127.0.0.1:81
log:
level: "info"
path: "/Users/zj/proxy.log"

// 运行服务
./goTcpProxy -c=etc/conf.yaml

gotcp


License


Copyright (c) 2015 released under a MIT style license.

三十分钟 Docker 新手入门

文章分享astaxie 发表了文章 • 0 个评论 • 615 次浏览 • 2016-10-10 23:35 • 来自相关话题

简介

如果您是 Docker 新手请您花大约三十分钟的时间来了解 Docker 相关的知识和内容。 Docker 与 Linux 息息相关,因此在阅读本文档之前请您确保以下条件:

  1. 对 Linux 的命令行操作有... 查看全部

简介


如果您是 Docker 新手请您花大约三十分钟的时间来了解 Docker 相关的知识和内容。 Docker 与 Linux 息息相关,因此在阅读本文档之前请您确保以下条件:



  1. 对 Linux 的命令行操作有一定了解,并且懂得一些基础命令。

  2. 对 Linux 服务管理有一定的了解。

  3. 当阅读完本文之后您可以了解什么是 Docker、使用它有什么好处、以及 Docker 具体的使用方法。


为什么选择 Docker?



  1. 相对于虚拟机来说 Docker 有镜像管理。

  2. 相对于虚拟机来说更强大的迁移能力。

  3. 云计算的未来,再也不用受到环境 API 的限制。


安装 Docker


Docker 的安装十分简单方便,如果您有 Linux 虚拟机 VPS 可以直接参考 Docker 极速下载 运行如下脚本来安装 Docker:


user$ curl -sSL https://get.daocloud.io/docker | sh

在 Docker 安装完成以后,国内的特殊网络环境会导致在构建 Docker 镜像或者是抓取(pull)来自 Docker Hub 上的镜像时会遇到连接问题,这时我们就需要使用 DaoCloud 加速器服务



  1. 注册 DaoCloud 账号,打开控制台进入加速服务。

  2. 在网页的最下方有详细的使用步骤请参考。


如果您使用的是 Windows 或 Mac 请您下载安装 Boot2Docker。


这里我推荐使用 Ubuntu Server 操作系统,无论是在裸机上还是虚拟机上。


Hello World


这部分的代码来自 Docker 用户指南,但是与用户指南不同的是我们会描述更多的执行过程。


案例 1 启动一个容器


root# docker run ubuntu:14.04 /bin/echo 'Hello world'
Hello world


  1. 注意操作 Docker 是需要权限的,这里使用 root 用户操作,当然您也可以把 docker 加入自己的用户。

  2. run 作为 Docker 的子命令来控制新建容器并且运行。Docker 命令虽然比较多,但是命令是分级来执行的,多参照 help 就会习惯。

  3. ubuntu:14.04 代表镜像的名字和版本号,托管在 Docker Hub 上,如果本地没有抓取过那么执行命令的时候会自动从 Docker Hub 抓取。

  4. /bin/echo 为在容器内执行的程序(应用)。

  5. 'Hello world' 为程序执行的参数。



提示:Docker 在命令执行完毕后不会销毁容器,但是状态为变为 Exited。



从这句命令中我们可以看到 Docker 可以根据情况判断镜像存在的情况,在后文中会介绍镜像的管理。同时 ubuntu:14.04 将会被载入到缓存中,如果后面的镜像构建依赖于它并不会花费额外的网络带宽抓取,十分方便。


案例 2-1 以交互模式启动一个容器


root# docker run -t -i ubuntu:14.04 /bin/bash
root@af8bae53bdd3:/#

与上面的案例不同的是这条命令带有 -t 和 -i 选项,这两个选项在这指的是:


-i, --interactive=false Keep STDIN open even if not attached
-t, --tty=false Allocate a pseudo-TTY

上面来自 docker help run 的输出,help 命令是获取文档帮助信息最简短而高效的途径。本次启动的进程是 bash,运行 bash 之后,容器会在交互的模式下启动。当 bash 退出后就会停止运行。这就是 Docker 运行程序的最简单方式。



提示:此程序运行完成之后,容器不会被销毁,但是状态为 Exited。此外对于以交互模式启动的容器可以先按下 Ctrl+P 然后按下 Ctrl+Q 这样的按键顺序脱离(detach)一个容器,脱离后的容器状态仍为 Up 并且程序会继续在后台运行,这时可以使用 attach 命令重新附到一个已经脱离的程序。


案例 2-2 查看容器内的文件以及容器本身



在上一个例子中如果容器没有关闭通过如下命令可以看出:


root@33d90ffaf1ac:/# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
root@33d90ffaf1ac:/# pwd
/
root@33d90ffaf1ac:/#

Docker 中整个容器是一个 Linux 环境,ubuntu 镜像的默认用户为 root,默认工作目录为根目录。


root@33d90ffaf1ac:/# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 18172 3104 ? Ss 08:50 0:00 /bin/bash
root 16 0.0 0.0 15572 2200 ? R+ 09:04 0:00 ps aux

容器中的进程相当简洁,只有正在运行的两个程序没有其他任何进程。


并且 PID 号码是独立存在的,与宿主机完全没有关系。


案例 3 查看后台运行容器的日志信息


root# sudo docker run -t -i -d ubuntu:14.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
8b8aad0aa7670441f99ce88fbac021bfb9cb124e7de4417a00ed3c0ccc6cb203

在这个案例中加入了新的选项 -d,这个选项中可以让 Docker 后台运行程序。


如果我们查看运行的结果:


root# docker logs 8b8aad0aa767
hello world
hello world
hello world

从上面的命令来看,使用 logs 能看到程序的输出 log 过程,这样对服务的调试是非常有帮助的。如果容器没有自己设定的名字很难快速准确的调度容器。


案例 4 快速准确的调度容器--给容器起名字


root# docker run -t -i -d --name helloubuntu ubuntu:14.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
8b8aad0aa7670441f99ce88fbac021bfb9cb124e7de4417a00ed3c0ccc6cb203

上面的命令跟之前案例中的命令对比多了一个 --name 选项给调度容器带来了很多方便,用户可以自己设定容器的名字,当然如果未指定名字系统会自动起一个随机的名字给容器。那么我们查看 logs 的时候就可以通过命令 docker logs helloubuntu 来查看日志信息了。


注意:容器名必须以英文字母和数字开头,并且只能包含英文数字、下划线 _、小数点 .、和减号 -。


案例 5 列出所有正在运行的容器和它们的基本信息


root# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
53d90769be11 ubuntu:14.04 [...] 2 min ago Up 2 min helloubuntu
8b8aad0aa767 ubuntu:14.04 [...] 14 min ago Up 14 min dreamy_fermat
deaaa8e60c9f ubuntu:14.04 [...] 14 min ago Up 1 sec distracted_darwin
33d90ffaf1ac ubuntu:14.04 [...] 26 min ago Exited (0) tender_franklin


注意:由于文档宽度关系,样例中有些内容被缩写以方便展示。



# docker ps -a 这个命令中我们可以看到容器的 ID、使用的镜像、启动的命令、创建的时间、当前状态、端口映射状态和名字。


案例 6 容器管理


以下管理命令都可以通过传入容器的 ID 或者名字来管理指定的容器:



  • stop 停止一个正在运行的容器。

  • start 运行一个正在停止的容器。

  • restart 重启一个容器。

  • rm 删除一个容器。


传入多个 ID 或者名字可以操作多个容器。例如:docker rm name1 name2 可以同时删除两个容器。



提示:如果要删除所有容器 docker rm $(docker ps -q -a)。



阶段总结


当您看到这里的时候可以说对 Docker 已经有了初步的操作能力,包括运行容器中的程序、查看容器内容、运行容器、停止容器、查看容器、重启容器和删除容器。但是距离在 Docker 上运行自己的业务或者组织开发还是有一定距离的。接下来我们开始进阶学习,重点研究一下 Docker 操作的组成要素。


Docker 组成要素


如果将轮船拉着集装箱运行在大海上,与 Docker 运行容器里面的程序类比:



  • Linux 相当于大海,有不同的海洋气候各有不同。

  • Docker 相当于能行驶在各种大海上的轮船,容器相当于各种规格的集装箱。

  • Docker 内的系统相当于货物的包装。

  • 目标程序则相当于货物。


当您看完上面的描述之后可以了解到每一种角色不同的作用以及所处的位置有所了解。


当然理论上的 Docker 只是一个轮船图纸,必须得有一个守护进程才能运行。


镜像也是一样,它只是集装箱的图纸,需要在 Docker 中运行容器。


通过 Docker 运行您的 Web 应用,Step By Step


代码文件请参考: DaoCloud 搭建静态博客 以及 整个研究过程参考


Step 1:准备自己的互联网应用 - 参考文件中的 static 这个文件为编译好的 golang 应用程序。


Step 2:准备 Linux RootFS - 参考文件中的 root.fs 为打包好的 BusyBox。


Step 3:准备 Dockerfile:


# 从一个空镜像中开始创建,没有任何依赖。
FROM scratch
MAINTAINER DaoCloud <example@daocloud.io>

# 给 Docker 文件系统中添加根目录,也是 Linux 的一些基础目录。
ADD ./rootfs.tar /

# 给镜像添加工作目录 /app
RUN mkdir -p /app

# 设定默认工作路径
WORKDIR /app

# 复制应用进入到镜像中
COPY ./static /app

# 复制应用依赖的静态文件目录
COPY ./public /public

# 对外开放的服务接口为 8080
EXPOSE 8080

# 容器运行时默认调用的启动命令
CMD ["/app/static"]

Step4:构建镜像 - sudo docker build -t [标签] . 注意:最后有一个点 . 表示构建时的当前目录。


Step5:运行镜像 - 这一步就不赘述了,请参阅最开始的一个章节。


这里重点讲解一下第三步中 Dockerfile 的格式。参照 Dockerfile 中的注释。


关于 Dockerfile 内部的指令的教程网上虽然很多,但是坑也不少。首先要注意,构建用的依赖文件都要放到同一个目录中,为了安全和可移植性尽量不要用到目录之外的文件。


COPY 或者 ADD 的目的地址比如说上面的 /app/ 与 /public 意义不同,第一个是复制文件到 app 下,第二个是复制目录到根名字叫 public。


为了更详细的介绍 Dockerfile 添加一个额外的案例:


FROM node:slim

MAINTAINER DaoCloud <example@daocloud.io>

RUN apt-get update \
&& apt-get install -y git ssh-client ca-certificates --no-install-recommends \
&& rm -r /var/lib/apt/lists/*

RUN echo "Asia/Shanghai" > /etc/timezone \
&& dpkg-reconfigure -f noninteractive tzdata

RUN npm install hexo@3.0.0 -g

RUN mkdir /hexo
WORKDIR /hexo

EXPOSE 4000

CMD ["/bin/bash"]

在这个 Dockerfile 中:


FROM node:slim 表示构建的时候依赖于 Node.js 系统,并标明了标签为 slim,但是没标明也不需要标明的是 node 依赖于 Debian,这是用户自己需要了解的,下一步就开始运行,当开始构建时是可以运行 Dockerfile 中的脚本的。


第一个 RUN:后面标记的是需要让 Debian 更新源并且安装一系列的软件,最后清空无用缓存。


第二个 RUN:设定时区。


第三个 RUN:安装 Hexo 博客系统。


最后新建目录设定工作目录开放 4000 端口上文中已经提过。


在补充的这个例子中我们可以清楚的看到构建自己的镜像可以依赖于其他镜像构建环境基本上都已经准备好了,非常方便。第二个例子是构建 Hexo 博客环境的做法。


管理 Docker 镜像


拿来主义:pull(抓取)


第一种是直接抓取比如说下载一个 docker 管理用的 dockerui 工具,我们可以直接告诉 docker pull 回来:


root# docker pull dockerui/dockerui

也可以通过运行的方式来抓回镜像:


root# docker run -d -p 9000:9000 --privileged \
-v /var/run/docker.sock:/var/run/docker.sock dockerui/dockerui

这样用直接访问 http://localhost:9000/ 的方法来管理 docker 了。


查看自己所有的镜像


root# docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
hexo3 latest 66f7fc371b44 9 days ago 261.5 MB
node slim 84326d4c0101 11 days ago 164.5 MB
dockerui/dockerui latest 9ac79962b9b0 2 weeks ago 5.422 MB
busybox latest 8c2e06607696 7 weeks ago 2.433 MB
garland/butterfly latest 114f9c134231 3 months ago 394.5 MB
progrium/busybox latest 8cee90767cfe 4 months ago 4.789 MB

搜索需要的镜像


推荐到 Docker Hub Registry 上搜索。


但是也可以通过命令 docker search 关键词 进行搜索。


清空所有当前镜像


使用命令:docker rmi $(docker images -q)


如果当前已经没有镜像了命令会报错找不到镜像。



警告:请谨慎清空所有镜像。



构建镜像的缓存


在构建镜像时 Dockerfile 中的指令会进入缓存,如果构建时可以使用缓存,docker 可以做到快速完成构建。


Docker 数据卷的挂载与外部服务访问


首先要注意的一点:容器被删除后里面的数据会被删除,因此要注意挂载数据卷使数据持久化。


因此这里介绍一下挂载宿主机中的目录作为数据卷到 Docker 中的方法:


root# docker run -it -d -p 4000:4000 -v /root/blog/:/hexo --name hexo hexo3
这是上一个案例中运行 Hexo 博客的方法,注意挂载描述在 -v 之后,容器中就可以访问到宿主机中的持久化位置了。



注意,在数据库应用中最需要根据配置文件将数据库持久化的位置放到宿主机中。



对于开发更加详细的意见可以产考 使用 Docker 做开发的建议团队工作流


为了使 Hexo 博客可以被宿主机以外的设备访问,这里使用 -p 参数来发布 Docker 的端口到宿主机中。


Docker 学习建议:



  1. 在详细实践完成本文之后如果您有精力,并且英文阅读能力还不错请您移步到 Docker官方文档 继续更深入的学习。

  2. Docker 只是一种非常实用的工具,不要以 Docker 为目的去学习 Docker,重要的不是 Docker 而是您用 Docker 做什么。


总结


由于篇幅有限新手教程就到这里,希望您在这半个小时到一个小时中能有一次非常完美的 Docker 学习体验,在接下来的学习中您还可以继续从 Docker 官方的文档中了解更多的 Docker 相关的信息,尤其是 Docker 容器与容器之间的问题解决,以及更多更加丰富的命令参数使用,比如环境变量的控制。


相信做完这部分实践之后您已经了解了 Docker 能够做什么,如何进行基础方面的使用。在下一篇中我们会 根据您现有的基础介绍如何使用 DaoCloud 各项服务。


在接下来的学习中,如果需要请您学习使用版本控制工具 Git,及其相关基础知识。

请教各位大神 golang 有根据设备mac 等信息 生产唯一标示的库吗

回复

有问必答astaxie 回复了问题 • 1 人关注 • 1 个回复 • 810 次浏览 • 2016-10-10 23:20 • 来自相关话题