【TIDB】3、数据库的发展历史、现在、未来
1、从单机数据库说起(mysql、oracle、postgresql)
关系型数据库起源自1970年代,其最基本的功能有两个:
-
把数据存下来;
-
满足用户对数据的计算需求。
第一点是最基本的要求,如果一个数据库没办法把数据安全完整存下来,那么后续的任何功能都没有意义。当满足第一点后,用户紧接着就会要求能够使用数据,可能是简单的查询,比如按照某个key来查找value;也可能是复杂的查询,比如要对数据做复杂的聚合操作、连表操作、分组操作。往往第二点是一个比第一点更难满足的需求。
在数据库发展早期阶段,这两个需求其实不难满足,比如有很多优秀的商业数据库产品,如oracle/db2。在1990年之后,出现了开源数据库mysql和postgresql。这些数据库不断地提升单机实例性能,再加上遵循摩尔定律的硬件提升速度,往往能够很好地支撑业务发展。
接下来,随着互联网的不断普及特别是移动互联网的兴起,数据规模爆炸式增长,而硬件这些年的进步速度却在逐渐减慢,人们也在担心摩尔定律会失效。在此消彼长的情况下,单机数据库越来越难以满足用户需求,即使是将数据保存下来这个最基本的需求。
2、分布式数据库之nosql的进击(hbase/cassadra/mongodb)
hbase是其中的典型代表。hbase是hadoop生态中的重要产品,google bigtable的开源实现。
hbase本身并不存储数据,这里的region仅是逻辑上的概念,数据还是以文件的形式存储在hdfs上,hbase并不关心副本个数、位置以及水平扩展问题,这些都依赖于hdfs实现。和bigtable一样,hbase提供行级的一致性,从cap理论的角度来看,它是一个cp的系统,并且没有更进一步提供 acid 的跨行事务,也是很遗憾。
hbase的优势在于通过扩展region server可以几乎线性提升系统的吞吐,及hdfs本身就具有的水平扩展能力,且整个系统成熟稳定。
但hbase依然有一些不足
- 首先,hadoop使用java开发,gc延迟是一个无法避免问题,这对系统的延迟造成一些影响。
- 另外,由于hbase本身并不存储数据,和hdfs之间的交互会多一层性能损耗。
- 第三,hbase和bigtable一样,并不支持跨行事务,所以在google内部有团队开发了megastore、percolator这些基于bigtable的事务层。jeff dean承认很后悔没有在bigtable中加入跨行事务,这也是spanner出现的一个原因。
3、分布式数据库之rdms的救赎(cobar、zebra、atlas、vitess)
rdms系统做了不少努力来适应业务的变化,也就是关系型数据库的中间件和分库分表方案。做一款中间件需要考虑很多,比如解析 sql,解析出shardkey,然后根据shardkey分发请求,再合并结果。另外在中间件这层还需要维护session及事务状态,而且大多数方案并不支持跨shard的事务。还有动态的扩容缩容和自动的故障恢复,在集群规模越来越大的情况下,运维和ddl的复杂度是指数级上升。
4、newsql的发展
2012~2013年google 相继发表了spanner和f1两套系统的论文,让业界第一次看到了关系模型和nosql的扩展性在一个大规模生产系统上融合的可能性。
spanner 通过使用硬件设备(gps时钟+原子钟)巧妙地解决时钟同步的问题,而在分布式系统里,时钟正是最让人头痛的问题。spanner的强大之处在于即使两个数据中心隔得非常远,也能保证通过truetime api获取的时间误差在一个很小的范围内(10ms),并且不需要通讯。spanner的底层仍然基于分布式文件系统,不过论文里也说是可以未来优化的点。
google的内部的数据库存储业务,大多是3~5副本,重要的数据需要7副本,且这些副本遍布全球各大洲的数据中心,由于普遍使用了paxos,延迟是可以缩短到一个可以接受的范围(写入延迟100ms以上),另外由paxos带来的auto-failover能力,更是让整个集群即使数据中心瘫痪,业务层都是透明无感知的。f1是构建在spanner之上,对外提供了sql接口,f1是一个分布式mpp sql层,其本身并不存储数据,而是将客户端的sql翻译成对kv的操作,调用spanner来完成请求。
5、spanner和f1的追随者
spanner/f1论文引起了社区的广泛的关注,很快开始出现了追随者。第一个团队是cockroachlabs做的cockroachdb。cockroachdb的设计和spanner很像,但是没有选择truetime api ,而是使用hlc(hybrid logical clock),也就是ntp +逻辑时钟来代替truetime时间戳,另外cockroachdb选用raft做数据复制协议,底层存储落地在rocksdb中,对外的接口选择了pg协议。
另一个追随者就是我们做的tidb。tidb本质上是一个更加正统的spanner和f1实现,并不cockroachdb那样选择将sql和kv融合,而是像spanner和f1一样选择分离。
和 spanner一样,tidb是一个无状态的mpp sql layer,整个系统的底层是依赖 tikv 来提供分布式存储和分布式事务的支持,tikv的分布式事务模型采用的是google percolator的模型,但是在此之上做了很多优化,percolator的优点是去中心化程度非常高,整个继续不需要一个独立的事务管理模块,事务提交状态这些信息其实是均匀分散在系统的各个key的meta中,整个模型唯一依赖的是一个授时服务器,在我们的系统上,极限情况这个授时服务器每秒能分配 400w以上个单调递增的时间戳,大多数情况基本够用了(毕竟有google量级的场景并不多见),同时在tikv中,这个授时服务本身是高可用的,也不存在单点故障的问题。
tikv和cockroachdb一样也是选择了raft作为整个数据库的基础,不一样的是,tikv整体采用rust语言开发,作为一个没有gc和 runtime的语言,在性能上可以挖掘的潜力会更大。不同tikv实例上的多个副本一起构成了一个raft group,pd负责对副本的位置进行调度,通过配置调度策略,可以保证一个raft group的多个副本不会保存在同一台机器/机架/机房中。
6、未来趋势
1、数据库会随着业务云化,未来一切的业务都会跑在云端,不管是私有云或者公有云,运维团队接触的可能再也不是真实的物理机,而是一个个隔离的容器或者「计算资源」
2、多租户技术会成为标配,一个大数据库承载一切的业务,数据在底层打通,上层通过权限,容器等技术进行隔离
3、olap和oltp业务会融合,用户将数据存储进去后,需要比较方便高效的方式访问这块数据,但是oltp和olap在sql优化器/执行器这层的实现一定是千差万别的。以往的实现中,用户往往是通过etl工具将数据从oltp数据库同步到olap数据库,这一方面造成了资源的浪费,另一方面也降低了olap的实时性。对于用户而言,如果能使用同一套标准的语法和规则来进行数据的读写和分析,会有更好的体验。
4、在未来分布式数据库系统上,主从日志同步这样落后的备份方式会被multi-paxos / raft这样更强的分布式一致性算法替代,人工的数据库运维在管理大规模数据库集群时是不可能的,所有的故障恢复和高可用都将是高度自动化的。
7、知识拓展
7.1、gps同步时钟工作原理
在最初的同步通信系统中,我们会找到一个时钟源,然后把所有的收发子系统都接到这个时钟源上。小型的同步通信系统完全可以这样做,比如一台电脑中的一个同步通信的系统,他们就用电缆线接到一个共同的时钟源上,再来收发信号。
可是一旦同步通信的系统变大到全国性的呢?如果还用电缆或者光缆接到同一个时钟源上,会发生很多问题。首先,建设的成本太大了,要在全国范围内铺设线路,只为传输一个时钟信号,不划算。其次,如果收发信机分别在黑龙江和广东,时钟信号即使以光速传过去,还会产生一定的延时。
每个gps卫星上都有2~3个高精度的原子钟,这几块原子钟互为备份的同时,也互相纠正。另外地面的控制站会定期发送时钟信号,和每一颗卫星进行时钟校准。
当然你可能会担心卫星信号传送到地面的延迟问题。gps信号中自带了误差纠正码,接收端可以很容易的把延迟的这段传输延迟去掉。另外,由于卫星信号很微弱,只有在室外才能接受的到,因此每个gps授时系统都应当有室外天线,否则就不能用了。
这样一来上面列出的两个问题都解决了。用来铺设全国性电缆并不是每家公司都有资金实力的,而且铺设的成本用来买gps接收器,那肯定可以买到无数个了。而延时的问题,也被gps出色的编码系统所解决了。真的是太完美了。
spanner是如何保证每个事务最后得到的commit timestamp介于这个事务的start和commit之间?
在事务开始阶段调用一次truetime,返回[t-ε1,t1+ε1],在事务commit阶段时再调用一次truetime,返回[t2-ε2,t2+ε2],根据truetime的定义,显然,只要t1+ε1<t2-ε2,那么commit timestamp肯定位于start和commit之间。等待的时间大概为2ε,大约14ms左右。可以说,这个延时基本上还可以接受。
7.2、hybrid logical clock(hlc)
每个cockroach节点都维持了一个混合逻辑时钟(hlc) ,相关的论文见 hybridlogical clock paper。hlc时间使用的时间戳由一个物理部件(看作总是接近本地物理时钟)和一个逻辑部件(用于区分相同物理部件上的事件)组成。它使我们能够以较少的开销跟踪相关联事件的因果性,类似于向量时钟(译注:vector clock,可参考leslie lamport在1978年发表的一篇论文《time, clocks, and the ordering of events in adistributed system》)。在实践中,它工作起来更像一个逻辑时钟:当一个节点收到事件时,它通知本地逻辑hlc由发送者提供的事件时间戳,而当事件被发送时会附加一个由本地hlc生成的时间戳。
cockroach使用hlc时间为事务选取时间戳。本文中,所有 时间戳 都是指hlc时间,hlc时钟在每个节点上是都是单一实例的(译注:也就是说每个节点上只有唯一一个hlc时钟,不会有两个时钟,产生两个时间的问题)。hlc时钟由节点上的每个读/写事件来更新,并且hlc 时间大于等于( >= )系统时间(wall time)。从来自其他节点的cockroach请求里接收到的读/写时间戳不仅仅用来标识操作的版本,也会更新本节点上的hlc时钟。这用于保证在一个节点上的所有数据读写时间戳都小于下一次hlc时间。