NewSQL数据库简介
NewSQL的未来
最近数据库圈的一个比较大的事件是 NoSQL 先驱之一的 RethinkDB 的关张大吉,RethinkDB这个事情本身我就不多做评论了,现在这个时机去分析不免有马后炮的嫌疑,今天我想借着这个引子谈谈新型数据库的未来。
纵观过去十年数据库的发展,其实是相当迅速,随着互联网的发展以及业务数据量的不断膨胀,大家渐渐发现传统的单机 RDBMS 开始出现一些瓶颈,很多业务的模型也和当初数据库设计的场景不太一样,一个最典型的思潮就是反范式设计,通过适当的数据冗余来减小 JOIN 带来的开销,当初 *-NF 的设计目的是尽可能的减小数据冗余,但现在的硬件和存储容量远非当年可比,而且存储介质也在慢慢发生变化,从磁带到磁盘到闪存,甚至最近慢慢开始变成另一个异构的分布式存储系统 (如 Google F1, TiDB),RDBMS 本身也需要进化。在 NoSQL 蓬勃发展的这几年也是 Web 和移动端崛起的几年,但是在 NoSQL 中反范式的设计是需要以付出一致性为代价,不过这个世界的业务多种多样,大家渐渐发现将一致性交给业务去管理会极大的增加业务的复杂度,但是在数据库层做又没有太好的可以 Scale 的方案,SPOF 问题和单机性能及带宽瓶颈会成为悬在业务头上的达摩克利斯之剑。
NewSQL 的出现就是为了解决扩展性和一致性之间的矛盾,我所理解的 NewSQL 主要还是面向 OLTP 业务和轻量级的OLAP业务,大型复杂的OLAP 数据库暂时不在本文讨论的范围。对于 NewSQL 来说,我觉得应该需要明确一下必备的性质,什么样的数据库才能称之为NewSQL,在我看来,应该有以下几点必备的特性:
1. SQL
2. ACID Transaction, 支持跨行事务
3. Auto-scale
4. Auto-failover
SQL
一切为了兼容性!兼容性!兼容性! SQL 的支持是和之前 NoSQL 从接口上最大的不同点,但是在一个分布式系统上支持 SQL 和在单机 RDBMS 上也是完全不一样的,更关键的是如何更好的利用多个节点的计算能力,生成更好的执行计划,将计算逻辑尽可能的均摊到多个存储节点上,设计这样一个 SQL engine 的更像是在做一个分布式计算框架,考虑的侧重点和单机上的查询引擎是不一样的,毕竟网络的开销和延迟是单机数据库之前完全不需要考虑的。不过从最近几年社区的实现来看,尤其是一些 OLAP 的系统,已经有不少优秀的分布式 SQL 实现,比如: Hive, Impala,Presto, SparkSQL 等优秀的开源项目。虽然 OLTP 的 SQL engine 和OLAP 的侧重点有所不同,但是很多技术是相通的,但是一些单机 RDBMS 的 SQL 是很难直接应用在分布式环境下的,比如存储过程和视图等。正如刚才提到的,越来越多的业务开始接受反范式的设计,甚至很多新的业务都禁止使用存储过程和外键等,这是一个好的现象。
ACIDTransaction
对于 OLTP 类型的 NewSQL 来说,最重要的特点我认为是需要支持 ACID 的跨行事务,其实隔离级别和跨行事务是个好东西,能用更少的代码写出正确的程序,这个交给业务程序员写基本费时费力还很难写对,但在分布式场景下支持分布式事务需要牺牲点什么。本身分布式 MVCC 并不是太难做,在 Google BigTable / Spanner 这样的系统中显式的多版本已经是标配,那么其实牺牲掉的是一定的延迟,因为在分布式场景中实现分布式事务在生产环境中基本就只有两阶段提交的一种办法,不过呢,考虑到 Multi-Paxos 或者 Raft 这样的复制协议总是要有延迟的,两阶段提交所带来的 contention cost 相比复制其实也不算太多,对于一个分布式数据库来说我们优化的目标永远是吞吐。延迟问题也可以通过缓存和灵活降低隔离级别等方法搞定。
很多用户在进行架构设计的时候,通常会避免分布式事务,但是这其实将很多复杂度转移给了业务层,而且实现正确的事务语义是非常复杂的事情,对于开发者的要求还蛮高的,之前其实没有太多的办法,因为几乎没有一个方案能在数据库层面上支持事务,但是有些对一致性的要求很高的业务对此仍然是绕不开的。就如同 Google 的 Senior Fellow Engineer,Jeff Dean 在去年的一个会议上提到,作为工程师最后悔的事情就是没有在BigTable 上支持跨行事务,以至于在随后的 10 多年,Google内部的团队前赴后继的在 BigTable 上造了多套事务的*以支撑业务,不过这个遗憾在 Spanner 中已经弥补了,也算是 happy ending。 其实 Spanner 的例子是非常好的,而且我认为 Google Spanner 及 F1 是第一个在线上大规模使用的 NewSQL 系统,很多经验和设计我们这些后来者是可以借鉴的。我认为,从使用者的角度来说,滥用事务带来的性能问题不能作为在数据库层面上不支持的理由,很多场景如果使用得当,能极大的降低业务开发的复杂度。
Scale
作为一个现代的数据库,可扩展性我觉得是排在第一位的,而且这里的可扩展性值得好好说一下,目前关系型数据库的扩展方案上,基本只有分库分表和 PROXY 中间件两种方案,但是这两种方案并没有办法做到透明的弹性扩展,而且到达一定规模后的运维成本几乎是指数级别上升,这也是大多数公司在微服务实践的过程中遇到的最大障碍,服务可以很好的做到无状态和解耦合,但是数据天然是有状态的,比较理想的情况是有一个统一的,通用的存储层,让服务层能够放开手脚。弹性扩展的技术在很多 NoSQL 里已经有过很好的实践,目前比较成熟的方案还是类 BigTable 式的 Region based 的方案,在 Key-Value 层面上实现水平扩展。另外值得一提的是,对于 NewSQL 这类需要支持 SQL 的数据库而言,region based 的数据切分是目前唯一的方案, 因为 SQL 的查询需要支持顺序 Scan 的数据访问方式,基于一致性哈希的方案很难支持高效的顺序访问。这里隐含了一个假设就是真正能够支持 Scale 的 NewSQL 架构很难是基于中间件的方案,SQL above NoSQL 是目前来说唯一的可行方案,从 Google 的选型也能看出来。基于中间件的方案的问题是中间件很难生成足够高效的执行计划,而且底层的多个数据库实例之间并没有办法提供一个统一的事务语义,在执行跨节点的 JOIN 或者事务的时候没有办法保证一致性,另外底层的数据库实例(一般来说是 PostgreSQL 或者 MySQL) 本身并没有原生的扩展方案,需要中间件层面上额外做大量的工作,这也是为什么中间件的方案很多,但是做完美非常困难。
Failover
目前大多数的数据库的复制模型还是基于主从的方案,但是主从的方案本质上来说是没有办法脱离人工运维的,所以这个模型是很难 Scale 的 。如果需要做 Auto failover,从库何时提升成主库,主库是否完全下线,提升起来的从库是否拥有最新的数据?尤其如果出现集群脑裂的状况,一旦 Auto failover 设计得不周全,甚至可能出现双主的严重故障。所以数据的一致性是需要 DBA 的人工介入保证,但是在数据量比较庞大的情况下,人工可运维的规模是有上限的,比如 Google 在 F1 之前,维护了一个 100 节点左右的 MySQL 中间件集群支持同样的业务,在这个规模之下维护的成本就已经非常高了,使得 Google 不惜以从零开始重新开发一个数据库为代价。从 Google 在 Spanner 和 F1 以及现在流行的开源 NewSQL 方案,比如 TiDB 和CockroachDB 等可以看到,在复制模型上都没有选择主从的模型,而是选用了 Multi-Paxos 或者 Raft 这样基于分布式选举的复制协议。
Multi-Paxos(以下出于省略暂用 Paxos 来指代,但是实际上两者有些不一样,在本文特指Multi-Paxos) 和Raft 的原理由于篇幅原因就不赘述了,简单来说,它们是高度自动化,强一致的复制算法,在某节点故障的时候,支持完全自动和强一致的故障转移和自我恢复,这样才能做到用户层的透明,但是这里有一个技术的难点,即Scale 策略与这样的复制协议的融合,多个 Paxos / Raft Group 的分裂、合并以及调度,以及相关的测试。这个技术的门槛比较高,实现起来的复杂度也比较高,但是作为一个 Scale 的数据库来说,是一定要实现的,在目前的开源实现中,只有 PingCAP 的TiDB 的成熟度和稳定度是比较高的,有兴趣的朋友可以参考 TiDB(github.com/pingcap/tidb) 的实现。
Failover相关的另外一个话题是跨数据中心多活,这个基本上是所有分布式系统开发者心中的圣杯。目前也是基本没有方案,大多数的多活的方案还是同步热备,而且很难在保证延迟的情况下同时保持一致性,但是 Paxos 和 Raft based 的方案给多活提供了一种新的可能性,这类选举算法,只要一个 paxos / raft group 内的大多数节点复制成功,即可给客户端返回成功,举一个简单的例子,如果这个 group 内有三个节点,分别在北京,廊坊,广州的三个数据中心,对于传统的强一致方案,一个在北京发起的写入需要等待广州的数据中心复制完毕,才能给客户端返回成功,但是对于 paxos 或者 raft 这样的算法,其实延迟仅仅是北京和廊坊之间数据中心的延迟,比传统方案大大的降低了延迟,虽然对于带宽的要求仍然很高,但是我觉得这是在数据库层面上未来实现跨数据中心多活的一个趋势和可行的方向。按照 Google 在 Spanner 论文中的描述,Google 的数据中心分别位于美国西海岸,东海岸,以及中部,所以总体的写入延迟控制在半个美国的范围内,还是可以接受的。
未来在哪里
不妨把眼光稍微放远一些,其实仔细想想 NewSQL 一直在尝试解决问题是:摆脱人工运维束缚,存储层实现真正的自生长,自维护,同时用户可以以最自然的编程接口访问和存储数据。当实现这点以后,业务才可以摆脱存储的介质,容量的限制,而专注于逻辑实现。大规模的分布式和多租户是必然的选择,其实这个目标和云的目标是很接近的,实际上数据库是云不可或缺的一部分,不过现在的数据库都太不 Cloud-Native 了,所以如何和云更紧密结合,更好的利用云的资源调度,如何对业务透明的同时无缝的在云上自我进化是我最近思考得比较多的问题。在这个领域 Google 是走在时代前沿的,从几个 NewSQL 的开源实现者都不约而同的选择了 Spanner 和 F1 的模型上可以看得出来。
NewSQL究竟新在哪里?
本文来自技术博客:http://oserror.com/
微信订阅号有置顶功能,欢迎读者置顶架构文摘:ArchDigest
近几年来,数据库领域出现了一种新的关系数据库类型,称为NewSQL,例如,Google的Spanner,Amazon的Aurora等等,这些数据库相对于传统数据库来讲,区别在哪里?What’s Really New with NewSQL?给了很好的总结,本篇文章主要是总结该论文的观点,最后会有一个简单的讨论部分,全文的组织结构如下:
-
为什么需要NewSQL?
-
NewSQL的分类
-
NewSQL的技术挑战有哪些?
-
讨论
本文收录在我的github中papers项目,papers项目旨在学习和总结分布式系统相关的论文。
为什么需要NewSQL?
数据库的发展通常是随着业务需求的变化,在2000年左右,随着互联网的兴起,有许多同时在线的用户,这对数据库领域带来了非常大的挑战,数据库通常会成为瓶颈,所以,此时业务针对数据库的需求,主要体现在可扩展上面。
这时期数据库的扩展性,往往采用如下两种方案:
-
垂直扩展:使用更好的硬件,来做数据库的服务器
-
水平扩展:采用中间件,做sharding的方式,即分库分表的方式
垂直扩展中使用更好的硬件意味者成本高,并且更换硬件后,需要把数据从老的机器迁移到新的机器,中间可能需要停服务,因此往往采用水平扩展,例如,Google’s MySQL-based cluster。
采用中间件方式也有缺点,中间件一般要求轻量级,简单数据库操作可以搞定,但是,如果需要做分布式事务或者联表操作,会非常复杂,通常这些逻辑会放到应用层来做。
后续,NOSQL兴起,主要有几个原因:
-
传统关系数据库更倾向于一致性,而在性能和可用性比较差
-
全功能的关系型数据库太重
-
关系模型对于简单的查询太重,不必要
NOSQL以Google’s BigTable 和 Amazon’s Dynamo为代表,开源版对应为HBase和Cassandra。
NOSQL往往是不保证强一致性的,而对于一些应用来讲(例如金融服务),是需要强一致性和事务的,因此,如果它们基于NOSQL系统来开发的话,应用层需要些大量的逻辑来处理一致性和事务相关的问题。此时,业务需求是拥有可扩展性的基础上,能够支持强一致性。
因此,这里有几条路:
-
性能更好的单个服务器来做数据库服务器
-
中间件层支持分布式事务
使用更好的单个服务器的话,不满足业务需求的可扩展性。
使用中间件的话,会有如下问题,例如:
-
中间件层往往是比较轻量级的,要实现一致性,必须在中间件层实现分布式事务,这点是非常困难的
-
中间件层本身的高可用很难保证
上面两条路都不能很好的满足应用的需求,因此,NewSQL出现了。
首先来看NEWSQL的定义:针对OLTP的读写,提供与NOSQL相同的可扩展性和性能,同时能支持满足ACID特性的事务。即保持NOSQL的高可扩展和高性能,并且保持关系模型。
NEWSQL的优点:
-
轻松的获得可扩展性
-
能够使用关系模型和事务,应用逻辑会简化很多
注意,此篇论文中的NEWSQL偏向于OLTP型数据库,和一些OLAP类型的数据库不同,OLAP数据库更偏向于复杂的只读查询,查询时间往往很长。
而NEWSQL数据库的特性如下,针对其读写事务:
-
执行时间短
-
一般只查询一小部分数据,通过使用索引来达到高效查询的目的
-
一般执行相同的命令,使用不同的输入参数
NewSQL的分类
分三大类:
-
从头开始,使用新架构的系统
-
中间件
-
DAAS,数据库即服务
New Architectures
采用新架构的NewSQL有如下特点:
-
无共享存储
-
多节点的并发控制
-
基于多副本做高可用和容灾
-
流量控制
-
分布式查询处理
优势:
-
所有的部分都可以为分布式环境做优化,例如查询优化,通信协议优化。例如,所有的NEWSQL DBMS可以直接在节点间发送查询,而不是通过中心节点,例如中间件系统
-
本身负责数据分区,因此,可以把查询发送给有数据的分区,而不是把数据发送给查询。
-
拥有自身的存储,可以指定更复杂的多副本的方式
缺点:
-
懂该数据库的人少,缺少专业的运维
代表产品:Spanner,CockroachDB
Transparent Sharding Middleware
中间件负责的事情如下:
-
对查询请求做路由
-
分布式事务的协调者
-
数据分布,数据多副本控制,数据分区
往往在各个数据库节点,需要装代理与中间件沟通,负责如下事情:
-
在本地节点执行中间节点发来的情况,并且返回结果
优点:
-
应用通常不需要做变化
缺点:
-
各个节点还是运行传统数据库,即以磁盘为核心的数据库,对现有的大内存,多核服务器难以高效地利用
-
重复的查询计划和查询优化,在中间件做一次,在各个DBMS做一次
备注:有研究表明,以磁盘为主要存储的传统DBMS,很难有效地利用非常多的核,以及更大的内存容量。
代表产品: MariaDB MaxScale, ScaleArc
Database-as-a-Service
特点:
-
用户可以按需使用
-
数据库本身可能使用云产品,例如云存储等,可以较容易的实现可扩展性
代表产品:
-
Amazon Aurora
-
ClearDB
NewSQL的技术挑战有哪些?
Main Memory Storage
传统数据库都是以磁盘为存储中心的架构,读盘操作相对较慢,一般是内存中缓存页。
现在来讲,内存较便宜,容量大,能存储大量的数据。这些纯内存操作带来的好处是,读取和写入数据速度较快。
现有的大内存服务器,对数据库对内存的管理提出了新的要求,不再是像传统数据库那样,只是用来做页缓存,可以采用更高效地内存管理方式。
Partitioning/Sharding
数据分区一般以某几列做hash或者range分区。
特点:
-
数据库需要能在多个分区执行SQL,并且合并数据结果的功能。
-
把同一个用户的数据可以放在一起,即使是不同数据表的数据,可以减少通信开销。
-
可以在线的添加或者删除机器。
-
可以在线的迁移或复制分区。
Concurrency Control
数据库通过Concurrency Control来提供ACID中的Atomicity和Isolation。
Atomicity
分布式场景下,一般采用类2PC的协议,根据事务是否需要中心节点,分为以下两类:
-
中心节点:单点,容量限制
-
非中心节点:需要时钟的同步
关于时钟同步,不同数据库也有不同的做法,Spanner和CroachDB在时钟同步上的不同选择:
1
|
But what makes Spanner differ- ent is that it uses hardware devices (e.g., GPS, atomic clocks) for high-precision clock synchronization. The DBMS uses these clocks to assign timestamps to transactions to enforce consistent views of its multi-version database over wide-area networks. CockroachDB also purports to provide the same kind of consistency for transactions across data centers as Span- ner but without the use of atomic clocks. They instead rely on a hybrid clock protocol that combines loosely synchronized hardware clocks and logical counters [41].
|
Isolation
现有实现Isolation的技术主要包括:
-
2PL:two phase locking
-
MVCC: Multiversion Concurrency Control
-
OCC: Optimistic Concurrency Control
大部分的数据库还是在选择使用MVCC,例如CockroachDB;有些数据库使用2PL+MVCC,修改数据的时候,还是采用2PL,例如,InnoDB,Spanner
Secondary Indexes
一般有两种实现方式:局部索引VS全局索引
局部索引:
-
每个partition有一部分索引数据,每次修改索引,只需要修改一个节点,但查找数据需要可能涉及多个节点
全局索引:
-
每个partition都有完整的索引数据,每次修改索引,都需要使用分布式事务,修改所有包含此索引副本的节点,查找数据只需要在一个节点
Replication
两个需要考虑的点:
-
如何保证一致性:Paxos和2PC(跨Partition)
-
同步的方式:采用同步执行命令的方式,还是同步状态的方式
Crash Recovery
如何最小化宕机时间?
采用主备切换
如何优化新加机器恢复到同步的时间?
一般手段为做checkpoint
讨论
可扩展性是NewSQL的一个非常重要的特点,对于中间件的方式,其上需要存路由信息,其本身的可扩展性比较难以解决,个人认为,其不应该算入NewSQL。
NewSQL的技术挑战除了上述提到的之外,还有如何实现多租户架构及租户之间的隔离,负载均衡等等问题。
从整篇论文中描述的内容可以看出,NewSQL中并没有开拓性的理论技术的创新,更多的是架构的创新,以及把现有的技术如何更好地适用于当今的服务器,适用于当前的分布式架构,使得这些技术有机的结合起来,形成高效率的整体,实现NewSQL高可用,可扩展,强一致性等需求。
版权申明:内容来源网络,版权归原创者所有。除非无法确认,我们都会标明作者及出处,如有侵权烦请告知,我们会立即删除并表示歉意。谢谢。
深入理解Raft及在NewSQL中的使用
本文整理自APMCon 2016中国应用性能管理大会 数据库性能优化专场 京东资深架构师 张成远 题为《深入理解Raft及在NewSQL中的使用》的演讲,现场解读Raft核心内容,剖析etcd Raft库的设计实现,针对此类复杂分布式系统算法的测试方法,并介绍业界主流NewSQL的详细设计,Raft在其中发挥的重要作用,以及如何使用等。
张成远:我是京东资深架构师张成远,目前主要是做分布式数据库相关的一些架构研发等工作。今天主要讲一下Raft在NewSQL的使用,因为内容涉及到Raft和NewSQL,我会先讲一下什么是NewSQL,什么是事务以及一致性等,还有NewSQL大概是怎么做的,然后再讲一下Raft是什么以及Raft是怎么样工作的。
一、What is NewSQL
从SQL到NoSQL再到NewSQL,一路走来大概经历过这样一个发展过程,一开始最早的时候是SQL类产品,后来慢慢有了NoSQL的东西,最近这一两年开始有NewSQL这个概念。SQL理论,也就是关系型数据库理论大概是上世纪70年代左右就是已经提出来了,这个理论下产生了很多的产品,像MySQL、Oracle、PostgreSQL、SQL Server等很多很多。
后来随着分布式系统的发展衍生了NoSQL这个概念,出现了很多的分布式存储系统像Redis、mongoDB、cassandra等,这些NoSQL类的产品特点是不支持SQL,主要以KV形式存储,在扩容方面非常容易,可以较为轻松的进行一些集群的伸缩等操作。NoSQL出来以后一直喊着要把SQL颠覆,后来在实际生产环境中我们发现这两者是一种长期并存的趋势,每类产品都有一定的适用场景。
等到后来,大家可能希望出现一种东西可以结合SQL跟NoSQL的优点,既支持SQL支持事务又可以比较容易的进行集群的伸缩扩容等操作,让海量数据方面的处理可以更加的容易,所以就出现了NewSQL的概念。NewSQL这个概念之下,国内外都有一些优秀的开源的项目在做像TiDB、CockroachDB等,但目前开源领域可能还没有在生产环境当中真正大规模使用的。如果要说真正落地使用的话可能当属google的F1/spanner了,但他们家没有开源只有论文流出。
二、What is transaction
然后,接下来讲一下NewSQL最大的特点,就是支持SQL同时支持事务,支持SQL比较好理解,相当于可以用SQL的方式来增删改查这个系统,至于支持事务那我们就要回到非常老的一个问题,到底什么是事务?支持事务,那事务到底是什么定义?事务有ACID属性,也就是原子性、一致性、隔离性还有持久性。
原子性是指整个事务要么成功,要么失败(跟没有发生过一样),不要有任何的中间状态。
一致性其实在一定程度上来说主要是数据库层面的完整性,打个比方A的帐户里面100块钱,B的帐户里面50块钱,然后,A给B 25块钱,首先是从A上减去25块钱,然后将这25块钱加到B上,最终都是75块钱。不能出现A减25块钱然后B的25块钱没有加上去,这样全局数据库完整性就不一致了,这个属性需要应用程序自己做更多的考虑,另外数据库层面保证了原子性、隔离性以及持久性的时候,外加应用程序自身的逻辑是正常的,那这个一致性就是可以保证的。
什么是隔离性呢,一般是指并发事务操作彼此之间不可见,简单的说就是A做操作的时候不要让B事务看到。
持久性,简单的理解就是事务commit了以后要保证这个事务是一直不会丢失的。比如你把A减了100块钱或者A加了100块钱,这个事情如果告诉用户已经成功了就是成功了,不能出现因为机器挂掉之类导致这种修改丢失。
还有一个概念也要提一下,这个也是一个比较老的概念,就是CAP。
很多时候会把CAP一致性和ACID的一致性混在一起,CAP一致性跟ACID的一致性其实是两回事情。CAP的一致性是针对很多个节点,要求各个节点之间的数据是完全一致的,也就是要求实际存储的数据是完全一致的一致性,也就是纯粹的多副本方式。
CAP里的A其实就是高可用,简单的理解就是随时去访问都可以访问到,CAP里的P是说在发生一些网络异常状况的时候是否可以容忍。在绝大多数的系统当中,理论上来说CAP是不能同时满足的,但在实际生产环境中往往选择弱化的P,也就是当发生网络异常时直接将一部分异常节点舍弃保证剩余节点的服务都是正常的从而保证了整体的服务,所以从这个角度上来说CAP是可以全部满足的,或者说满足生产需求已经足够。
上图是我们在做分布式数据库的时候一般的一些做法,很多的时候直接基于一些现有的SQL类产品,比如MySQL,外加一层数据库中间件的方式比如引入Proxy的方式来解决这个分布式数据库问题,SQL发过来以后由Proxy接收然后解析拆解,再把相应的SQL发到相应的SQL产品上,支持简单的事务。为什么叫做简单的事务呢,因为如果只涉及到一个分片是可以的,但是多个就是支持不了的,确切的说是因为不能保证严格的分布式事务的语义,后面会详细讲一下。
三、NewSQL
那么NewSQL是怎么样的?NewSQL有很多种可能的实现方式,这里列举NewSQL其中的一种架构模式,如上图,上层兼容SQL协议的一个协调者,然后通过RPC之类的通信方式和底层存储通信,存储节点可能会选择Rocksdb之类的K/V存储系统。为什么说上图这个是一个协调者,但看到的效果看起来也像一个代理像一个Proxy?他的区别在于可以做很多的事情,可以对这个事务进行一些记录,记录到日志里面,还可以提供全局的事务ID之类,保证同一个大事务里的各个节点上的子事务ID可以和全局事务ID是一样,后面会详细讲一下。
一般来说,NewSQL要支持SQL同时有NoSQL的优点,这个从实现上面来说给用户的感觉就是SQL产品,SQL过来以后协调者会把它拆成各种各样的KV操作,丢到各个节点上去,相当于丢到多个分片上,但同一个分片上有很多个节点,这些节点之间要保证数据是一致的,这样可以做到这个分片上万一有节点挂了,还有另外的节点可以对外提供服务。
1、隔离性
那怎么保证同一个分片内多个节点之间的数据是一致的呢?Raft就是用来保证各个节点是一致的。为什么上图Rocksdb这层要引入Range的概念呢?因为作为一个节点可能数据量会非常大,后期要支持扩容怎么扩?如果这个节点不拆分是没有办法扩的,所以要引入Range这个逻辑概念。需要扩容的时候就可以把Range进行拆分成多个,每一个Rocksdb上面放很多的Range,然后把这些Range迁移到其他的Rocksdb上面去,相当于通过拆分加迁移方式同时达到扩容整个集群容量的目的,每一个Range相当于一个分片里的节点,多个Range通过Raft构成一个分片,同一个分片里的Range的数据是一样的。
现在我们再回过头去讲一下,为什么基于现有的SQL类产品开发一个Proxy的方式其实是没有办法保证严格的分布式事务的,有一种场景很容易理解:假设以MySQL为例,如果一个事务里有多条SQL,这些SQL如果涉及到不同的MySQL实例,可能会出现一个分片的MySQL上提交成功了,另一个分片的MySQL挂了,那就会出现事务只提交一半的情况,这种情况就保证不了原子性。
如果有一个分片的MySQL挂了,事务的原子性无法保证这种场景是比较好理解的,但是其实就算所有的分片上的MySQL都是正常的也是无法严格保证事务的,为什么呢?因为前面讲过ACID里有一个隔离性属性,并发事务之间要求是不可见的,而基于MySQL中间件的方式是保证不了隔离性的。
如上图,假设有4个事务是同时发生的,对于理解就是并发过去的到Proxy节点的时候实际上肯定是有一个顺序的,这个顺序我们假设是T1、T2、T3、T4个顺序,如果4个事务涉及到4个分片,4个事务同时落到四个片的MySQL上但实际上在每个MySQL上这四个事务的执行顺序可能是不一样的,每个MySQL上会有自己的一个执行序列,也就是这些事务在每个MySQL上会组成一个可串行化调度的序列,所谓可串行化调度就是并发的事务操作会按照一定的顺序执行,执行以后的效果等价于这些事务按照某个顺序串行执行的效果,那么这个调度序列就是可串行化调度的,而这些等价的串行顺序可能是一样的也可能是不一样的。比如这四个事务T1、T2、T3、T4在分片一上的执行顺序可能是T1、T3、T2、T4,分片二上的执行顺序却是T4、T2、T1、T3,分片三的执行顺序是T1、T2、T3、T4,而分片四上的执行顺序是T1、T4、T2、T3。
在这种情况下,分片2上就会出现事务T1会看到T4和T2的修改,而在分片1、3和4上,事务T1事务是先执行的,所以并不会看到T4和T2的修改,这就相当于在全局范围内,事务T4和T2的部分修改被T1读到了,而另一部分却没有被T1读取到,出现了T1和T2及T4之间的隔离是有问题的,所以在全局范围内是无法保证严格事务之间的隔离性的。
再举个例子如图所示,假设变量a和b都是5,此时有事务T1和T2且全局执行顺序是T1和T2,假设每个分片上都是按照T1和T2的方式执行,结果最终结果a和b都是14,此时是符合事务的可串行化调度的。
但是可能出现一种情况,在分片1上的执行顺序是T2、T1,分片2上的执行顺序是T1、T2,此时分片1上a就变成了15,而分片2上就变成了14, 这就不符合可串行化的条件了,因为从全局来说T1和T2是混在一起的,即不等价于T1T2的执行顺序也不等价于T2T1的执行顺序。怎么解决这个问题呢,这时一般会采用引入一个全局的事务ID,在实际执行的时候可能会采用时间戳的方式,具体细节这块今天先不展开讨论了。
2、原子性
前面讲了隔离性问题,现在我们再讲一下原子性,在保证分布式事务的时候要支持分布式事务语义,那ACID 4个属性都要满足,前面介绍了隔离性,那原子性这个怎么解决呢?在实际实现的时候往往采用类似两阶段提交的方式,我这里画的是两阶段提交的理论上的定义,全局会有一个协调者,事务要提交的时候协调者会发送请求到各个参与节点,参与节点确认可以提交了就会回复给协调者表示通过,如果参与节点表示这个事务因为一些原因不能提交就会给协调者反馈说这个事务需要回滚,那么协调者就会根据这些反馈信息来综合决定事务是否需要提交。只有所有的参与节点都表示可以提交,协调者才会在自己先写日志提交这个事务,然后发送commit命令到所有的参与节点,如果出现任何一个参与节点表示需要回滚,那么协调者就会在先写日志表示abort这个事务,然后把abort信息发送给每个节点。图上的每一个圈都是一个系统的状态,是一个典型的状态机,其中两层圆圈表示的终止状态,整个状态机最终会到达终止状态上,状态机的东西在后面我也会再提一下。
3、持久性
讲完NewSQL里怎么支持原子性和隔离性,我们再讲一下持久性。同时也要提一下CAP里的一致性,刚才已经说了CAP的一致性和ACID里的一致性不是一个概念,CAP里的一致性是解决多个节点副本之间数据一致性的问题,好比MySQL的主从数据完全一样,那就符合CAP里的一致性,从这个层面上来说CAP里的一致性其实对应事务里的持久性。
为什么这么说呢,大家想一个例子,还是以MySQL的主从为例来说明。主从主要可以解决高可用的问题也可以解决数据备份的问题,如果主从之间假设没有数据的复制,那么在主挂了或者出异常以后,从接替主开始对外服务相当于保证了CAP里的A,但是之前所有已经提交的事务都丢失了,因为主从之间没有进行数据复制,相当于丢失了ACID里的持久性。
假设主从之间有数据复制但是有延后,那么当发生主从切换的时候,依然会存在事务丢失的情况,因为可能会出现有几个事务在主上已经提交了,但是还没有复制到从上,此时主挂了从开始担任主对外提供服务依然保证了CAP里的A,但是依然会存在已经提交的事务丢失的情况。所以从这个角度来说CAP里的多个节点之间的一致性本质上是对应ACID属性里的持久性,如果不追求事务的持久性,那也就不用关心各个节点之间的数据是否一致了。
既然已经明确了CAP里的一致性本质上对应了持久性这个属性,那接下来的问题就是这个一致性也就是ACID里的持久性怎么做呢,对MySQL来说可能有半同步的复制方式,但是半同步在实际使用当中,如果真的出现一些问题比如网络问题或者机器性能问题等最终可能会变成异步复制,并不能保证严格的一致性。
所以在NewSQL中一般会考虑采用Raft来保证一致性。如上所示各个Node里有很多的Range,各个Range之间通过Raft来保证彼此之间的一致性,多个Range组成的Raft组合相当于是一个分片,Raft保证了这些Range之间的一致性从而保证了事务在同一个节点的多副本上的持久性。
四、What is raft
在真正开始介绍Raft之前,我想先引入一个状态机的介绍,因为前面的部分以及后面的部分都会涉及到状态机,那什么是状态机呢,需要简单介绍一下。
1、State machine
其实状态机可以简单的理解就是一个五元组,状态机可以记录为M={Q, ?,δ, q0, F},其中Q是状态集合;?是输入符号,在实际工程中也可以理解为具体的事件;δ是转移函数,表示在某个状态下接收了某个符号会进入到什么状态;q0是整个状态机的可能的起始状态;F是整个状态机的终止状态,在实际表示的时候一般会用上层的圆圈表示,终止状态的理解更恰当的说是指相对稳定的状态,所以并不是说到了终止状态以后整个状态机就不工作了,比如图上这个状态机的效果就是接收了1*0+(0|1)*的字符串。之所以在这里提一下状态机到底是一个什么东西是因为前面的两阶段提交就是一个状态机,后面的Raft也是到处都是状态机。
现在接下去讲一下什么是Raft,Raft简单的说就是相比传统的Paxos算法来说是一个更简单且比较容易理解,在工程上也比较容易实现的一种一致性算法,通过选举leader以及日志复制的方式完成工作,具体的实现会有一些措施来确保整个算法是可以安全工作的,比如如何保证操作复制到多数节点才生效,再比如出现异常时少数节点上有的日志在大多数节点上可能是没有的这种情况,如何通过算法将这些日志清除掉。
上图是Raft工作的示意图,Client发送请求到Leader上,consensus部分可以理解为Raft的主要工作部分,请求先发送到consensus模块,consensus部分会把操作先记录到本地日志中,然后同时将这个操作复制到大多数其他节点上去,等确认大多数节点已经接收到了这个操作,然后会把这个操作apply到上层的状态机,在实际实现的时候可以理解为把这个操作真正的应用到K/V存储上去,比如Rocksdb。
2、Server State
一个Raft组合相当于有很多节点,里面除了leader的角色还会有其他的角色,上图是一个角色转换图,一个新加入的节点首先是一个follower的角色,如果follower加入的时候发现没有leader就会变成Candidate的角色,发起选举准备竞选leader,如果这个candidate节点收到了大多数节点的投票,那么它就可以顺利的成为leader开始对外提供服务。但是如果此时没有选举成功且整组里还是没有leader,那么这个candidate会继续进行选举。如果这个candidate发起选举的时候发现已经有其他的节点成为了leader,那么它就会回到follower角色,这是一个典型的一个状态机,其中follower是起始状态,而follower和leader可以理解为终止状态,candidate是普通中间状态,也就是每一个节点的稳定状态最终一定会在停留在这两个状态中的一个,终止状态不代表一旦进入就要永远停留在这个状态,确切的说应该理解为一种相对稳定的状态。
3、Term & log index
在介绍日志复制之前,需要介绍两个概念,Term和Log index,Term相当于一个抽象的逻辑时间单位,每个Term里只有一个leader,如果在这个Term里没有发生网络抖动或者leader出异常的情况也就是没有发生新的选举的情况,那么这个Term一直保持不变,leader也一直保持不变。Log index是指每条操作命令在记录到日志的时候所在日志的位置,可以将log理解为一个很大的数组,log index就像是这个大数组的下标,往Log里每添加一条操作,log index就加1。在添加操作的时候要同时记录该操作对应的index以及Term,所以会出现不同操作命令对应的Term是一样的情况,比如图上x=1这个操作Term是1,log index是1,y=1这个操作Term是2,log index是2,假如在此时发生了一次选举,后面的操作的记录情况可能出现如图上所示的情况,x=7这个操作Term=2,log index是3,y=5这个操作Term=2, log index是4,x=2这个操作Term=2,log index是5,假设此时又发生了选举,每次选举的时候Term都会往上增加,此时Term变成了3但是因为一些原因这个Term内选举没有成功,当这次选举失败以后发现还没有leader就继续将Term增大到4继续进行选举,所以后面的y=3的操作记录的时候对应的Term就是4,log index=6,紧接着之后y=6的操作Term=4,log index=7,后续其他操作的记录方式跟这个类似。
4、Log replication
下面我们再以日志复制的具体例子来分析一下日志是怎么在Raft的各个节点之间进行复制的。假设有3个节点,leader上有这些日志,从图上可以看到已经记录了有12条操作,方框里的数字是Term,从图上可以看到这个系统已经发生过几次选举,但是这个leader后来重新选举的时候又继续成为了leader,方框上面的数字代表的是log index,可以看到log index是依次递增的。这些所有的操作会被复制到所有的follower上,在leader这端相当于会维护一个信息记录每个follower都复制到哪里了,比如这个图上所示follower1已经复制到log index为6的位置了,而follow2已经复制到log index为9的位置了,因为leader自己已经到了log index为12的位置,那么在全局来说log index为9以及9之前的操作都已经复制到了大多数节点上,所以9这个位置及之前的操作都可以安全的apply到上层的状态机,在实际实现的时候可以理解为可以把这个操作安全的应用到Rocksdb上,而9这个位置也就是commit index, commit index代表的是这个log index所在位置及以前的操作在全局范围内已经复制到了大多数节点上是不会丢失的,本质是满足持久化条件的,所以可以安全的将这些操作真正的生效。
日志复制里还有两条非常关键的特性,在同一个Raft组里不同节点日志上的操作如果log index和Term是一样的,那么对应的操作一定是一样的;另一个是如果不同节点日志上的某个位置拥有相同的log index和Term,那么表示在这个位置之前的所有的操作都是一样的。
具体讲一下这个是怎么做到的,这里简单举个例子,上图示意的是一个leader和其中一个follower的情况,假设这个follower是follower1,从图上可以看出来Term为1的时候leader是leader,Term为2和3的时候,follower1成为了leader,不过从Term=4之后原来的leader又重新成为了leader,并且后来可能因为网络抖动等原因又发生了一次选举,leader节点依然被选为了leader,所以会看到Term=5的时候leader这个角色保持不变。
此时出现了一种什么情况呢,在follower1上我们可以看到Term=2和Term=3的时候对应的log index依次为4、5、6,而这三条操作在leader的日志里没有。这是一种什么样的情况呢,有可能的一种情况是在Term=2和Term3的时候,follower1成为了leader,把这三个操作记录到了自己的日志中,但是还没来得及复制到其他节点上结果因为网络抖动或者自身异常等一些原因,原来的leader重新发起选举又成为了leader。这个时候原来leader对外正常提供了服务,且日志里记录了Term=4及以后的操作,那这个follower1在接收leader日志的时候有一部分日志和leader的日志就是不一样的,这部分日志我们之前也已经提过需要剔除掉的。
那怎么剔除掉这部分日志呢?在这个follower重新连上leader的时候,leader会把当前自身的<serverid, currentITerm, preLogTerm, preLogIndex> 信息传给follower1, preLogTerm和preLogIndex分别是leader当前日志里的最后一条日志的信息,在这个图中假设暂时没有新的发生选举,那么此时leader传给follower1的currentITerm、preLogTerm、preLogIndex分别是5,5,12。Follower1收到消息以后会检查自己日志的信息,一看自己当前的preLogTerm和preLogIndex分别是3和6,明显是不对应的,所以follower会告知leader自己当前的lastLogIndex,也就是自己当前日志的index位置,图中的情况follower1告诉leader自己的last log index是7,此时leader会直接到自己log index为7的位置准备把信息发给follower1,同时会把7位置之前的6位置的Term和log index信息发送给follower1,在leader上6位置的Term=4,而在follower1上6位置的Term=3,说明此时还是没有匹配上,于是leader会继续把该复制给follower1的日志的起始位置继续往日志起始的方向挪动,一直到log index=3的时候Term=3刚好对应上,那说明此时leader和follower1在log index=3之前的位置记录都是一样的,可以从log index=4的位置开始将操作记录一条一条的复制给follower1,从而做到即使因为一些极端的情况某些日志不一致了也可以将没有被复制到的大多数节点上的日志是安全清除。
最后小结一下,今天主要是讲了一下NewSQL怎么支持分布式事务,根据事务ACID的属性分析了一下,原子性主要是采用两阶段提交的方式来保证,一致性的话其实和传统的数据库的一致性一样,主要是数据库整体的完整性,隔离性的话可以考虑采用全局时间戳这类的方式实现,持久性的话采用的Raft的方式也就是对应CAP里的一致性。最后还讲了Raft师怎么工作的,最主要的就是两部分,一个是leader选举,一个是日志复制。
关于APMCon:
2016中国应用性能管理大会(简称APMCon 2016)于8月18日至19日在北京新云南皇冠假日酒店隆重召开。APMCon由听云、极客邦和InfoQ联合主办的作为国内APM领域最具影响力的技术大会,首次举办的APMCon以“驱动应用架构优化与创新”为主题,聚焦当前最为热门的移动端、Web端和Server端的性能监控和管理技术,整个会议设置包含了:性能可视化、服务端监控实践、运维自动化、数据库性能优化、APM云服务架构和HTML5调优最佳实践等话题,致力于推动APM在国内的成长与发展。
About Me
.............................................................................................................................................
● 本文整理自网络
● 本文在itpub(http://blog.itpub.net/26736162/abstract/1/)、博客园(http://www.cnblogs.com/lhrbest)和个人微信公众号(xiaomaimiaolhr)上有同步更新
● 本文itpub地址:http://blog.itpub.net/26736162/abstract/1/
● 本文博客园地址:http://www.cnblogs.com/lhrbest
● 本文pdf版、个人简介及小麦苗云盘地址:http://blog.itpub.net/26736162/viewspace-1624453/
● 数据库笔试面试题库及解答:http://blog.itpub.net/26736162/viewspace-2134706/
● DBA宝典今日头条号地址:http://www.toutiao.com/c/user/6401772890/#mid=1564638659405826
.............................................................................................................................................
● QQ群:230161599 微信群:私聊
● 联系我请加QQ好友(646634621),注明添加缘由
● 于 2017-07-01 09:00 ~ 2017-07-31 22:00 在魔都完成
● 文章内容来源于小麦苗的学习笔记,部分整理自网络,若有侵权或不当之处还请谅解
● 版权所有,欢迎分享本文,转载请保留出处
.............................................................................................................................................
● 小麦苗的微店:https://weidian.com/s/793741433?wfr=c&ifr=shopdetail
● 小麦苗出版的数据库类丛书:http://blog.itpub.net/26736162/viewspace-2142121/
.............................................................................................................................................
使用微信客户端扫描下面的二维码来关注小麦苗的微信公众号(xiaomaimiaolhr)及QQ群(DBA宝典),学习最实用的数据库技术。
小麦苗的微信公众号 小麦苗的QQ群 小麦苗的微店
.............................................................................................................................................