本文选自Andrew Pavlo(卡内基梅隆大学计算机科学系副教授)和Matthew Aslett(451研究所副总裁)在2016年所发表的论文What’s Really New with NewSQL。以下内容为伴鱼技术团队翻译,录信数软进行了二次整理和编辑。
摘要
近年来出现了一种称为NewSQL的新型数据库管理系统(DBMS),它们号称有能力扩展现代在线交易处理(on-line transaction processing,OLTP)系统的工作负载,这对于以前的系统来说是无法做到的。
鉴于传统的DBMS已经发展了近40年,有必要仔细推敲一下NewSQL的优势是真如他们所说,还是仅仅是一种商业宣传行为。如果真的可以获得更好的性能,那么下一个问题自然就是它们是真的有技术突破,还是仅仅因为硬件方面的发展使得原来的问题已不再是瓶颈?
为了探讨这些问题,我们先讨论了数据库的发展历史,以此理解NewSQL出现的背景和原因。然后从一些细节方面深入讨论了NewSQL的概念,特点,分类,以及在各个分类下面的NewSQL系统。
一、数据库管理系统(DBMSS)的简要发展历史
世界上第一个数据库系统,IBM IMS诞生于1966年,它被用于存储土星五号(Saturn V)和阿波罗(Apollo)空间探索项目所需的零件和供应商信息。IMS 的主要贡献在于展示了“应用程序逻辑与数据操作逻辑应该分离”的理念,应用程序开发者只需要关注数据的逻辑变化,而无需关心其具体实现。在IMS之后,出现了第一批关系型数据库,其主要代表就是IBM的System R系统以及加州大学的INGRES,即PostgreSQL的前身。INGRES迅速在其它大学的信息系统中流行起来,并于70年代末商业化。大约在相同的时期,Oracle采用类似System R的设计,开发并发布其DBMS的第一个版本。在80年代初期又涌现了一批公司,它们也推出自己的商业化数据库产品,如Sybase和Informix。在System R之后,IBM在1983年发布了新的关系型数据库DB2,后者复用了System R的部分代码,但二者至今未开源。
从80年代末到90年代初,面向对象的语言开始流行,这也催生了一批面向对象的DBMS诞生,以期磨平数据库模型与语言之间的隔阂。然而由于没有类似SQL一样的标准接口,这些面向对象的DBMS始终没有在市场上被广泛接受,不过它们的一些设计理念逐渐被融合进关系型数据库,许多流行的关系型数据库都增加了对Object、XML和JSON数据的支持。除此之外,面向文档(document-oriented)的NoSQL数据库也或多或少是面向对象的DBMS的延伸。
90年代的一个大事件就是两个开源关系型数据库的发布,MySQL和PostgreSQL。MySQL于1995年在瑞士诞生,主要基于ISAM的mSQL系统开发;PostgreSQL于1994年启动,由两名伯克利的学生对QUEL的Postgres源码进行二次开发,增加SQL查询语言的支持。
从2000年后,互联网应用如雨后春笋般出现,这些应用对各种资源的要求都远超传统的软件服务。互联网应用需要支持大量用户的并发访问,且对可用性要求极高,最好永远在线。在实践中,数据库开始成为互联网应用的瓶颈。许多厂商尝试纵向扩容,提高单机硬件性能,但这种方式换来的提升十分有限,表现出明显的边际收益递减。而且纵向扩容通常很不平滑,将数据从一台机器移动到另一台机器需要长时间下线服务,这对于互联网用户来说无法接受。为了解决这个问题,一些公司定制化开发中间件(middleware),将数据分片到多个普通单机DBMS上:
对上层应用抽象出一个逻辑数据库,而背后则将数据分散到不同的物理数据库上。当应用发起请求时,这层中间件需要转发或重写这些请求,分发给背后数据库集群的一个或多个节点,待这些节点执行完请求返回数据后,前者再将数据聚合返回给上层应用。从这个思路出发构建的两个比较著名的系统分别是eBay的Oracle-based cluster和Google的MySQL-based cluster。后来Facebook也采用类似的策略构建内部的MySQL cluster 并沿用至今。尽管利用中间件对数据分片的策略,可以处理简单的点读、点写操作,但如果要在一个事务中更新多条数据,或者多表join就变得十分困难。正因为如此,这些早期的中间件都不支持这类操作,eBay 要求这些中间件的用户必须在应用层逻辑中完成join逻辑。显然这违背了“应用程序逻辑与数据操作逻辑应该分离”的理念,将数据操作逻辑重新暴露给应用开发者。
最终基于中间件的分片方案被逐渐抛弃,各个公司将精力转向自研分布式数据库。除中间件方案暴露出的问题之外,传统数据库解决方案还暴露出两个问题:
第一,传统数据库注重一致性和正确性,在可用性和性能上有所牺牲。但这种trade-off与注重并发和可用性的互联网应用的需求背道而驰。
第二,传统关系型数据库的许多功能在互联网应用中并不适用,而支持这些功能却会消耗额外的资源,如果能够使用更轻量的数据库也许能提升整体性能。除此之外,关系模型也许不是表达应用数据的最佳方式,使用完整的SQL来完成简单查询似乎是“杀鸡用牛刀”。
这些问题正是2005-2010年间NoSQL运动的起源。NoSQL的拥趸普遍认为阻碍传统数据库横向扩容、提高可用性的原因在于ACID保证和关系模型,因此NoSQL运动的核心就是放弃事务强一致性以及关系模型,拥抱最终一致性和其它数据模型 (如 key/value,graphs 和Documents)。两个最著名的NoSQL数据库就是Google的BigTable和Amazon的Dynamo,由于二者都未开源,其它组织就开始推出类似的开源替代项目,包括Facebook的 Cassandra (基于BigTable和Dynamo)、PowerSet的 Hbase(基于BigTable)。有一些创业公司也加入到这场NoSQL运动中,它们不一定是受BigTable和Dynamo的启发,但都响应了NoSQL的哲学,其中最出名的就是MongoDB。
在21世纪00年代末,市面上已经有许多供用户选择的分布式数据库产品。使用NoSQL的优势在于应用开发者可以更关注应用逻辑本身,而非数据库的扩展性问题;但与此同时许多应用,如金融系统、订单处理系统,由于无法放弃事务的一致性要求被拒之门外。一些组织,如Google,已经发现他们的许多工程师将过多的精力放在处理数据一致性上,这既暴露了数据库的抽象、又提高了代码的复杂度,这时候要么选择回到传统DBMS时代,用更高的机器配置纵向扩容,要么选择回到中间件时代,开发支持分布式事务的中间件。这两种方案成本都很高,于是NewSQL运动开始酝酿。
二、NewSQL的兴起
本文认为NewSQL是对一类现代关系型数据库的统称,这类数据库对于一般的OLTP读写请求提供可横向扩展的性能,同时支持事务的ACID保证。换句话说,这些系统既拥有NoSQL数据库的扩展性,又保持传统数据库的事务特性。NewSQL重新将“应用程序逻辑与数据操作逻辑应该分离”的理念带回到现代数据库的世界,这也验证了历史的发展总是呈现出螺旋上升的形式。
在21世纪00年代中,出现了许多数据仓库系统 (如 Vertica,Greeplum 和AsterData),这些以处理OLAP 请求为设计目标的系统并不在本文定义的NewSQL范围内。OLAP 数据库更关注针对海量数据的大型、复杂、只读的查询,查询时间可能持续秒级、分钟级甚至更长。NewSQL数据库设计针对的读写事务有以下特点:
- 耗时短
- 使用索引查询,涉及少量数据 (非全表扫描或者大型分布式 Join)
- 重复度高,通常使用相同的查询语句和不同的查询参数
也有一些学者认为NewSQL系统是特指实现上使用Lock-free并发控制技术和share-nothing架构的数据库。所有我们认为是NewSQL的数据库系统确实都有这样的特点。
三、NewSQL的分类
既然已经定义NewSQL,我们就可以分析一下如今整个NewSQL数据库的图景。现今市面上的NewSQL数据库大抵可以分为3类:
- 完全使用新的架构重新设计开发的NewSQL数据库
- 在中间件层实现NewSQL特性的数据库
- 云计算平台提供的数据库即服务产品(DaaS),通常也是基于新的架构
在撰写本文之前,两位作者都将利用“替换单机数据库存储引擎” 的方案划分到NewSQL中。这种方案的典型代表就是一些替换MySQL、InnoDB的方案,如 TokuDB,ScaleDB,Akiban,deepSQL,MyRocks 的等等。使用新存储引擎的好处在于对于应用来说这样的替换毫无感知。但现在,两位作者收回了之前的观点,他们认为通过替换存储引擎和使用插件的方式扩展单机数据库系统并不是NewSQL系统的典型代表,不属于本文的谈论范围。通常,通过替换MySQL存储引擎来提升数据库OLTP 场景下性能的方案最终都难逃失败。
接下来我们将分别讨论这3类NewSQL数据库。
1.新型架构
从无到有搭建的NewSQL系统最令人感兴趣,因为项目设计有最大的自由度而无需考虑陈旧系统在设计、架构方面的负担。所有属于这类的数据库系统都是基于 shared-nothing的分布式架构,同时包含以下模块:
- 多节点并发控制 (multi-node concurrency control)
- 多副本数据复制 (replication)
- 流量控制 (flow control)
- 分布式查询处理 (distributed query processing)
使用新的数据库系统的另一优势在于各个组件都可以针对多节点环境作出优化,如查询优化器、节点之间的通信协议等等。一个具体的例子就是,大多数的NewSQL数据库都可以在节点之间直接传递同一查询 (intra-query) 内部的数据,而无需像一些基于中间件的方案需要通过中心节点路由数据。
除了Google的Spanner之外,属于这个类别的数据库通常都会自己管理存储模块,这意味着这些DBMS也需要负责将数据分布到不同的节点上,而不是采用开箱即用的分布式文件系统 (如 HDFS),或 storage fabric (如Apache Ignite)。这是很重要的设计决定,自己管理意味着“send the query to the Data”,而依赖三方存储则意味着“bring the Data to the query”,前者传递的是查询命令,后者传递的是数据本身,显然前者对于网络带宽资源消耗更加友好。自行管理存储层也使得数据库系统能够使用更精致、高效的复制策略,而不局限于block-based的复制策略。总得来说,自行搭建存储层可以获得更高的性能提升。
使用新的架构并非没有缺点,其最大的缺点就是它的技术过新导致用户担心这些技术背后还有许多坑尚未被填平,这也进一步意味着使用新系统的用户群体过小,不利于产品本身的打磨。除此之外,一些已经被广泛接受的运维、监控、报警生态也需要从头做起。为了避免此类问题,一些数据库系统,如 Clustrix、MemSQL、TiDB,选择兼容MySQL的通信协议;又如 CockroachDB 选择兼容 PostgreSQL通信协议。
例子:Clustrix、Cockroach DB、Google Spanner、H-Store、HyPer、MemSQL、NuoDB、SAP HANA、VoltDB、TiDB 等等。
Cockroach DB 架构
2.透明的数据分片中间件
市面上也有一些产品提供与当年eBay、Google、Facebook 以及其它公司类似的中间件解决方案,并支持ACID。在中间件之下的每个数据库节点通常:
- 运行相同的单机版数据库系统
- 只包含整体数据的一部分
- 不用于单独接收读写请求
这些中心化的中间件主要负责路由请求,协调事务执行、分布数据、复制数据以及划分数据到多节点。通常在每个数据库节点中还有一层薄薄的通信模块,负责与中间件通信、代替中间件执行请求并返回数据。所有这些模块合起来共同向外界提供一个逻辑单体数据库。
使用中间件的好处在于其替换现存数据库系统十分简单,应用开发者无感知。在中间件方案中最常见的单机数据库就是MySQL,这意味着为了兼容MySQL,中间件层需要支持MySQL通信协议。尽管Oracle提供了MySQL Proxy 和Fabric 工具帮助大家兼容,但为了避免GPL license的潜在问题,大部分公司都选择自行实现协议层的兼容。
基于中间件方案的缺点在于其依赖了传统数据库。传统数据库普遍采用以磁盘为中心 (disk-oriented) 的设计架构,它们诞生于20世纪 70年代,因此这类方案无法使用一些NewSQL系统使用的以内存为中心 (memory-oriented)的设计架构,从而也就无法有效利用其更高效的存储管理模块和并发控制模块。之前的一些研究已经表明,以磁盘为中心的架构设计在某种程度上限制了传统数据库更高效地利用更多的 CPU 核以及更大的内存空间。同时,对于复杂的查询,中间件方案有可能会引入冗余的查询计划和优化 (中间件一次、数据库节点一次),尽管这种做法也可以看成是查询的局部优化。
例子:AgilData Scalable Cluster、MariaDB MaxScale、ScaleArc 以及 ScaleBase。
Mariadb逻辑架构
3. Database-as-a-Service(DaaS)
许多云计算供应商提供了NewSQL的数据库即服务产品。使用这类服务,开发团队就没有必要在私有的硬件或者从云服务商处购买的虚拟机上维护数据库系统,而是由云服务商接管数据库的配置、调优、复制、备份等等。云服务商的用户只需要通过给定的URL访问数据库系统,或者利用控制面板和API来管理系统。
DBaaS的消费者根据其资源利用情况来支付。由于不同的数据库查询使用的计算资源可能大相径庭,因此DBaaS供应商通常不会像采用块存储服务一样根据查询次数的计费方式,而是让消费者确定其最大的资源使用限制 (如存储空间、计算资源和内存使用等) 来提供服务保证。比较出名的属于DBaaS的NewSQL系统是Amazon的Aurora,它既兼容MySQL的通信协议又兼容PostgreSQL的通信协议,其背后使用基于Log-structureD的存储管理模块来提高 I/O的并行效率。
也有一些公司依托于大型云服务商提供的云平台服务构建DBaaS解决方案,如ClearDB,它们可以被部署在各大云服务商的基础设施之上。这种方案的好处是可以在同一区域将数据库分布在不同的提供商上,来减少故障风险。
例子:Amazon Aurora、ClearDB。
Amazon Aurora云上部署架构
四、NewSQL的现状
本节我们讨论NewSQL数据库系统各模块的设计思路,来看看它们在实践和理论上是否有创新之处。
1.主内存存储(MainMemory Storage)
传统DBMS使用的是disk-oriented存储设计,数据主要存储在块存储设备,如SSD或HDD。由于在块存储设备上读写速度慢,这些DBMS会将读入的数据块缓存在内存中,也将待写出的数据缓存在内存中,通过批量写出提高效率。由于内存与磁盘相比,容量更小,价格更昂贵,这种设计极大地降低计算机的成本。几十年后的现在,内存的价格和容量都得到了改善,除了一些极大的OLTP 数据库,绝大多数数据库都可以被完全装入内存中,这使得DBMS可以快速地从内存中读出数据,甚至不再需要像缓存管理 (buffer pool manager)、重量级并发控制机制这些模块。从disk-oriented转向memory-oriented赋予DBMS性能优化新的可能性。
许多NewSQL数据库都采用基于内存的存储架构,不论是学术界的实验性数据库,如 H-Store,HyPer;还是业界的商用数据库,如MemSQL、SAP HANA 和VoltDB。这些系统的性能在OLTP 负载下的性能表现都要优于基于外存的存储架构。
实际上,将整个数据库装进内存的观点并不新鲜,威斯康星大学麦迪逊分校在20世纪80年代就已经在内存数据库领域打下了基础,覆盖了索引、查询处理、数据恢复等各个方面。在同一年代,第一个分布式内存数据库,PRISMA/DB完成开发。到了 90年代,第一批商用内存数据库问世,包括Altibase、Oracle TimesTen以及AT&TDataBlitz。
Altibase内存数据库架构
memory-oriented的NewSQL数据库中的创新点在于尝试将数据库中比较不活跃的数据清出内存,从而减少内存使用,这使得这些NewSQL数据库能够存储比它内存空间更大的数据而无需回到Disk-oriented的存储架构。广义地说,这类做法都需要建立一个内部跟踪机制来发现内存中不活跃的记录,在必要时将其清出到外存。以 H-Store为例,它的anti-caching模块会将不活跃的记录清出,并在其原来的位置放置一个 tombstone记录,当某个事务想要访问这条记录时,就将事务中止,然后起一个独立的线程去异步加载记录放回内存中。当然直接使用操作系统的虚拟内存的 paging机制也能达到相同的目的,如VoltDB。为了避免在利用索引查找时产生误判 (false negative),所有的二级索引都需要保留被清出记录的 key,如果应用所用的数据表有较多的二级索引,那么即便记录被清出,依然会造成内存浪费。Microsoft的 Siberia 项目,在解决这个问题时利用了bloom filter 来判断目标记录是否存在,以此来减少内存消耗。尽管 Siberia 不是NewSQL数据库,但这种做法值得参考。
MemSQL,另一个NewSQL数据库,没有跟踪记录的元信息,而是采用了不同的方案来解决数据量比内存大的问题。MemSQL将数据按log-structured的形式组织,使得数据写入的性能提升。除此以外,管理员可以手动地告诉数据库将某个表按列 (columnar format) 存储。
总体来看,基于内存的存储方案并没有显著创新,可以看作是以前方案的延伸。
2.分区/分片(Partitioning/Sharding)
所有NewSQL数据库都是通过将整个数据库划分成不同的子集,即partitions或shards,来实现横向扩容。
在分布式数据库上操作数据并不是一件新鲜事,PhiL Bernstein和他的同事早在 70年代末期就已经在这方面有所建树,他们的 SDD-1项目在分布式事务处理上做出了许多基础贡献。80年代早期,System R 和 INGRES 的开发团队就各自构建了相应的分布式数据库系统:IBM 的 R* 采用了类似 SDD-1的 share-nothing和disk-oriented的设计。INGRES 的分布式版本中提出了一种查询优化算法,可以递归地将查询动态拆分成更小的查询,为世人所铭记。后来 Wisconsin-Madison大学的GAMMA 项目探索了不同的分片策略。
然而,这些早期的分布式数据库系统都没有得到长远发展,主要有两个原因:
第一,20世纪的计算机硬件价格昂贵,绝大多数公司无法负担。
第二,高性能、高可用的互联网应用在20世纪并不存在,那时候数据库的QPS普遍在几十到几百之间
但如今,这两个假设都不再成立,在各种云基础设施的平民化,开源分布式系统、工具的帮助下,搭建一个数据密集型的应用变得比过去简单很多,也使得分布式数据库回到历史舞台。
在分布式数据库中,数据表会被按照某个字段或某组字段 (partitioning attributes),横向切分成多个分段 (segments),可能通过哈希函数来切分,也可能基于范围进行切分。多个数据表的相关联的数据分段常常会被合并起来放到同一个分片 (节点) 中,该节点负责执行任何需要访问该分片内部数据的请求。不过DBaaS(Amazon Aurora,ClearDB) 并不支持这种分片策略。理想情况下,分布式数据库应该能够自动将查询分布到多个分片上执行,然后将结果合并,除了 ScaleArc,其它NewSQL数据库都提供这样的支持。
许多OLTP 应用的数据库 schemas 都可以被转化成一个树状结构:
按照root table的主键散列,可以使得每个查询涉及到的数据都在一个分片中。比如有一张客户表,数据库按客户 id分片,那么这个客户的所有订单记录、账号信息都存储在同一个分片上,这样几乎所有事务都能够在同一个分片上执行,减少数据库系统内部节点间的交流,也不需要承担如两部提交 (2PC) 的成本,提高整体性能。
有一部分NewSQL数据库中的节点并不是等价的,如NuoDB 和MemSQL。NuoDB 会使用集群中的一个或多个节点负责存储管理 (storage manager, SM),每个SM存储着一个数据分片,在SM内部,数据被进一步划分成Blocks。集群中的其它节点负责执行事务 (transaction engine,Te),每个TE节点中缓存事务涉及到的Blocks。这是一个典型的计算、存储分离的设计。执行事务时,一个Te节点需要从SM或其它Te节点中获取事务涉及的所有Blocks,然后TE需要获取待修改数据的写锁,在本地修改完数据后将其广播给对应的SM和其它TE节点。为了减少Blocks 在各TE节点间来回传递的现象,NuoDB 在负载均衡策略上作了文章,使得具有局部性的Blocks 尽量分布到相同的Te上,这种思路使得NuoDB 既能够将数据合理分片,又不需要对数据表有任何假设 (如上文中的树状结构)。MemSQL也采用了与NuoDB 类似的计算、存储分离的设计,但与NuoDB 不同的是,MemSQL的计算节点 (aggregator) 并不缓存任何数据,而是将完整的查询拆成小查询让各个存储节点 (leafNode) 执行,即计算节点是无状态的。NuoDB 和MemSQL的计算和存储都具备独立横向扩展的能力。异构节点架构是否在性能、运维复杂度等各个方面上优于同构节点架构,目前尚未有定论。
NewSQL数据库的一个重要特性就是支持数据的线上迁移 (live migration),它允许数据库在不同的物理节点上重平衡 (rebalance) 数据,缓解热点,以及扩容的同时,不影响数据库本身的服务。相较于NoSQL,NewSQL要做到这点难度更大,因为后者还需要保证ACID的特性不被破坏。总体上看,NewSQL有两种方案来支持数据的线上迁移:
一是将数据组织成粗粒度的逻辑分片,散列到物理节点上。需要重平衡时,就在这些节点之间移动这些逻辑分片。这种方案在 Clustrix、AgilData、Cassandra 和DynamoDB 中都有应用。
二是在更细的粒度上,如记录 (tuple) 或一组记录 (groups of tuple) 上,通过取值范围来重排数据。MongoDB、ScaleBase、H-Store采用了这种方案。
3.并发控制(Concurrency Control)
并发控制机制是数据库系统处理事务最核心、最重要的部分,因为它涉及到几乎整个系统的方方面面。并发控制让不同的用户访问数据库时好像是单独占有一样,它提供了事务的原子性和隔离性保证,影响着系统的整体行为。
除了使用哪种并发控制机制,分布式数据库系统的另一设计关键点在于使用中心化还是去中心化的事务协调协议 (transaction coordination protocol)。在中心化协议下,所有事务操作的起点都是中心化的协调器 (coordinator),由后者来决定是否同意操作;在去中心化协议下,每个节点都维持着访问自身数据的事务状态信息,这些节点需要与其它节点相互通信、协调来确定是否有并发冲突。一个去中心化的协调器对于扩展性更加友好,但通常要求不同节点的墙上时钟高度同步,以便于确定事务的全序。
首批分布式数据库系统诞生于20世纪 70年代,它们普遍使用了两部加锁 (two phase locking,2PL) 机制。SDD-1是第一个在 share-nothing集群下支持分布式事务的数据库,它使用了中心化的协调器。IBM 的 R* 类似 SDD-1,不过它采用的是去中心化的协调机制,分布式2PL,每个事务都会将其访问的数据加锁。INGRES 数据库的分布式版本也是使用分布式2PL,但它依赖于中心化的死锁检测机制。
由于解决死锁问题过于复杂,几乎所有NewSQL数据库都抛弃了2PL。当前的流行的是timestamp ordering(TO) 并发控制机制的不同变体,在该机制中数据库假设:不会按可能造成违背serializable ordering的操作顺序来执行并发事务。在NewSQL系统中最流行的并发控制协议是去中心化的MVCC,当更新操作发生时,数据库会为每条记录创建新版本。保持一条记录的多个版本可以让读事务不阻塞写事务、写事务也不阻塞读事务。使用去中心化MVCC机制的NewSQL数据库包括MemSQL、HyPer、HANA 及 Cockroach DB,尽管它们都或多或少地根据需要做出定制化的改动,但这种方案的核心概念并不算创新。早在1979年,MIT的一篇 PhD毕业论文中就已经提到MVCC,而在80年代早期,第一批使用MVCC的商用数据库随即问世,它们包括DigitaL的 VAX Rdb 和Jim Starkey研发的InterBase,后者也是NuoDB和MySQLFalcon存储引擎的作者。
常见并发机制的分类
其它数据库系统使用的则是2PL和MVCC的融合方案,在这种方案中,写事务仍然需要按照2PL机制去获取数据的锁,每当事务修改一条记录,数据库也会按照MVCC的方式为该记录创建新版本;读事务则无需获取锁,因此也不会阻塞写事务。这种方案最著名的实现就是MySQL的 InnoDB,但它也被Google Spanner、NuoDB 以及 Clustrix 采用。NuoDB在原始的MVCC基础上通过节点间的Gossip协议来广播记录的版本信息,改善性能。所有中间件和DBaaS方案都继承了其背后单机数据库的并发控制机制,由于它们中的大部分使用的是MySQL,因此它们自然而然地也采用了2PL和MVCC混合的机制。
本文认为GoogleSpanner(包含其后代F1和 SpannerSQL) 实现的并发控制机制是所有NewSQL系统中最新颖的,尽管它是基于2PL与MVCC的混合机制,但与众不同的是Spanner通过硬件设备 (GPS、原子钟) 获得了高精度的时钟同步特性,并利用这些高度同步的时钟来为事务产生时间戳,获得事务的顺序,从而在广域网上实现多版本数据库的数据一致性。CockroachDB 也支持相同的事务一致性,但它没有使用这些硬件设备,而是依赖于基于低同步的时钟和逻辑计数器的混合时钟协议来实现。
到论文发表为止,唯一一个未使用MVCC变体的商用NewSQL数据库就是VoltDB,它仍然使用的是TO并发控制,其中每个分片上的事务按照一次只能执行一个的方式调度,而非像MVCC那样将事务中的操作交织在一起。在VoltDB中,单分片事务以去中心化的方式调度,跨分片事务则按照中心化的方式调度。VoltDB将事务按照逻辑时间戳排序,然后在分片上依次执行,当一个事务在某个分片上执行时,它独占整个分片的所有数据,因此该系统无需处理更细粒度的加锁逻辑。这种基于分片并发机制的缺点在于,如果事务涉及多个分片,而协调器与这些分片之间的网络出现延迟,那么这些分片都将空转而无法处理其他请求。基于分片的并发控制机制也不是新思路,Hector Garcia-Molina 在1992年的一篇论文中已经提出类似的变体方案,并在90年代末的kdb以及H-Store(VoltDB的学术前身) 中得到实现。
总体来看,NewSQL数据库使用的核心并发控制机制没有显著的创新,主要是老方法在现代硬件和分布式环境中的工程化。
4.次级索引(Secondary Indexes)
支持二级索引对于单机数据库来说并非难事,因为数据都在一个节点上,然而对于分布式数据库来说并非易事。例如:假设有一张客户表,按照客户 iD分片到不同的物理节点上。当有查询想要根据客户的邮箱地址来查时,就需要去每个节点上都查一遍,才能得到正确结果。
一个分布式数据库要支持二级索引,需要考虑两个设计决定:
- 将二级索引存放在哪里
- 如何在事务中维护二级索引
如果一个系统中存在中心化的协调器,如中间件方案,那么可以将二级索引存放在协调器节点和分片节点上,这种方案的好处就是全局只有一个版本的索引数据需要维护。
使用新架构的NewSQL数据库通常使用分片二级索引方案,即每个节点都存储索引的一部分,而不是整个索引存放在单独的一个节点上,在需要时复制到其它节点。这里的权衡很好理解:
Clustrix将上述两种方案结合:将二级索引按范围分片,每个节点都存着范围与分片的对应关系。遇到查询、更新请求时,都先将请求路由给合适的节点,再由后者执行相应的操作。这种两层的设计结合了两个方案的优点。
如果NewSQL数据库不支持二级索引,开发者的常见做法就是自己构建二级索引,并放在分布式缓存系统中,但依赖外部系统将使得索引与数据之间的行为没有一致性保证,开发者需要谨慎对待。
5.复制(Replication)
想要确保应用的可用性、数据的持久性,最好的方法就是在数据库层面实现复制。所有的现代数据库,包括NewSQL系统,都提供某种形式的数据复制机制。
在数据库复制上,有两个重要的设计决定:
如何保证跨节点的数据一致性?在一个强一致 (strongly consistent) 的数据库系统中,新写入的数据必须被相应的所有复制节点持久化之后,事务才能被认为已经提交。这样所有的读请求都能被发送到任意复制节点上,他们接收到的数据可以认为是最新的数据。强一致的数据库系统固然很好,但DBMS维持这样的同步状态需要使用像2PC 这样的原子提交协议 (atomic commitment protocol) 来保证数据同步,如果在这个过程中有一个节点故障或者出现网络分区,数据库服务就会没有响应。这也是为什么NoSQL系统通常使用弱一致性 (weekly consistent) 或最终一致性 (eventual consistent) 模型,在弱一致性保证下,通常Master节点无需等待所有复制节点持久化数据就可以告诉认为事务以及提交。所有我们所知道的NewSQL系统都支持强一致性数据复制,但这些系统如何实现强一致性并没有什么新意,数据库系统的 state machine replication早在20世纪 70年代就已经存在基础研究,在 80年代问世的NonStop SQL就是第一个使用强一致复制的分布式数据库系统。
如何执行跨节点数据传播?主要有两种执行模式。第一种被称为Active-active复制,即让每个复制节点都执行相同的请求。例如,接收到一个新请求,数据库系统会在所有复制节点上执行相同的请求;第二种是Active-passive复制,即请求先在一个节点上执行,再将状态传递给其它复制节点。大多数NewSQL数据库系统采用active-passive复制,这主要是因为每个请求到达不同节点的顺序不同,如果直接使用active-active复制,很容易导致数据不一致出现。相较之下,deterministic DBMSs,如 H-Store、VoltDB、ClearDB,都使用Active-active复制,因为在deterministic DBMS中,事务在不同节点上的执行顺序能够保证一致。
NewSQL与之前的数据库系统在工程上不同的一点在于,前者还考虑了广域网 (wide-area network) 的复制。在云服务流行的时代,多地多中心的应用部署已经不是难事,尽管NewSQL能支持广域网数据同步,但这需要使用者自行保障DC之间的网络质量。Spanner和CockroachDB 论文发表截止前提供广域网数据同步一致性优化唯二的两个数据库。Spanner采用了原子钟与GPS硬件时钟的组合方案,CockroachDB则采用混合时钟的方案。
6.崩溃恢复(Crash Recovery)
NewSQL数据库系统的另一个重要特性就是故障恢复机制。一般传统数据库的容错能力主要强调的是保证数据持久化,而NewSQL在此之上,还需要提供更高的可用性,即发生故障还能保证数据库服务的正常使用。
在传统数据库中,故障恢复的实现通常是基于WAL,即在故障后重启系统,载入最后一个checkpoint后回放WAL,使得系统能够回到故障前的正确的状态。这种方案被称为ARIES,最早由 IBM 的数据库研究人员与1990年前后发明,如今几乎所有数据库系统都采用ARIES 方案或其变种。
在存在复制节点(replicas)的分布式数据库中,传统的故障恢复方案并不能直接使用,其原因在于:master节点崩溃后,系统将选择一个replica作为新的Master节点,当老的Master节点回到线上,由于线上集群已经写入许多新数据,仅靠 checkpoint和WAL来恢复无法跟上集群,它需要从新的Master节点上同步所有新的改动。目前,主要有两种实现方案:
第一,利用本地checkpoint和WAL先恢复数据,然后从Master或其它复制节点拉取新的Logs。只要故障恢复后的节点处理Logs的速度比数据写入的速度快,最终肯定能够跟上整个集群。对于使用物理日志 (physical/physiological logging) 的数据库来说跟上整个集群是可能的,因为直接将数据的更新同步到本地比执行SQL速度要快很多。
第二。放弃本地的 checkpoint和WAL机制,直接从集群中的某个节点全量拉取数据,这种方案的好处在于同样的机制可以用于扩容集群节点。
通常基于中间件和DBaaS的NewSQL系统会基于单机数据库的故障恢复机制,增加额外的基础设施,如Leader选举等,来实现其所需的管理能力。而基于新架构的NewSQL系统会选择抛弃单机数据库的故障恢复机制,使用开箱即用的日志复制组件,如ZooKeeper、Etcd,或自己实现背后的算法,如Paxos、Raft。
以上所述的方案和技术组件早在 90年代之后就已经存在。
五、未来趋势
未来的数据库系统应该要能够在新产生的数据上执行分析型查询和机器学习算法,这种workload通常被称为real-time analytics 或 hybri dtransaction-analytical processing(HTAP),能够同时从历史数据和新数据中攫取洞见和知识。传统的商业分析和商业智能通常只能在历史数据上分析,但数据的价值通常在产生时最高,并逐渐随着时间的推移而减少,因此如果能够将数据生产和分析的间隔减小,将能够产生更大的价值。
目前有3种支持HTAP workload的方法:
最常见的方法是部署两套独立的数据库,一套用于处理OLTP workload,称为前端数据库;另一套用于处理OLAP workload,称为后端数据库。前端数据库处理新事务产生的数据,而在后台,系统管理用ETL工具将数据从OLTP数据库导入后端数据库,通常会是一个数据仓库。应用在后端数据库上执行OLAP查询,避免拖慢前端OLTP系统,而在OLAP系统产生的新数据则会反向推给前端OLTP数据库,形成闭环。
另一种盛行的设计方案是lambda architecture。即使用另一套批处理系统,如 Hadoop、Spark,在历史数据上计算复合的视图,同时使用一套流式处理系统,如 Storm、Spark Streaming等在新产生的数据上计算准实时的视图。批处理系统通常需要周期性的重新扫描数据集合,将计算结果通过流式处理系统再反馈给线上服务。
上述两种方案的一个共同特点就是将数据导向异构系统,这也带来了许多问题。首先,将数据修改传播到另一个系统需要时间,且时长通常以分钟或小时计,这就直接导致数据的计算无法实时;其次,部署和维护两套系统的运维成本也很高,通常这种人力成本能占到总成本约 50%;开发人员如果想要同时根据两套系统中的数据分析,则需要写两套数据获取逻辑。尽管同样使用异构系统,也有一些方案尝试向外隐藏两套系统的事实,只暴露一套接口,但这通常需要在背后将数据从OLTP系统 (如Hbase) 复制到OLAP系统 (如Spark) 上。
第三种方案就是使用一个HTAP 数据库,即支持高吞吐、低时延的OLTP workload,同时支持在历史 (冷) 数据和新 (热) 数据上运行逻辑复杂、时间长的OLAP workload。OHAP 系统与过去通用数据库的主要不同点在于,前者将一些特殊的OLTP 实现方案 (in-memory storage、lock-free execution) 和OLAP (columnar storage、vectorized execution) 方案结合。
SAP HANA和MemSQL是第一批宣称自己是支持 HTAP workload的NewSQL数据库。HANA通过在内部使用不同的执行引擎来实现,一个引擎用于行存储数据,适用于OLTP workload,一个引擎用于列存储数据,适用于OLAP workload;MemSQL使用不同的存储管理器 (SM) 存储数据,一种用于行存储,一种用于列存储,在执行引擎层将二者结合在一起。HyPer从使用与 H-Store类似的并发控制、行存储数据方案转型成使用MVCC、列存储方案,以便支持更复杂的OLAP查询。VoltDB也将它们的市场策略从纯OLTP转向支持流式计算。类似地,S-Store则尝试基于H-Store架构增加流式处理能力。甚至一些在21世纪00年代的以OLAP为目标的系统 (如Greenplum),也开始增加对OLTP的支持。
尽管HTAP数据库的兴起意味着单体大型OLAP数据仓库的终结,但短期内这并不会发生,因为目前这些数据仓库还是目前大部分公司的通用后端数据库,存放着公司所有的历史数据,但总有一天,满足OLAP workload将不再需要通过移动数据来实现。
END
更多干货内容请关注微信公众号“录信数软”~