作者:王磊 2018-05-07 09:30:41
数据库
分布式 在上一篇文章《从架构特点到功能缺陷,重新认识分析型分布式数据库》中,我们完成了对不同“分布式数据库”的横向分析,本文我将讲述拆解的第二部分,会结合NoSQL与NewSQL的差异,从纵向来谈谈OLTP场景“分布式数据库”实现方案的关键技术要点。
成都创新互联公司提供成都网站设计、成都网站制作、网页设计,高端网站设计,1元广告等致力于企业网站建设与公司网站制作,十多年的网站开发和建站经验,助力企业信息化建设,成功案例突破上千余家,是您实现网站建设的好选择.
在上一篇文章《从架构特点到功能缺陷,重新认识分析型分布式数据库》中,我们完成了对不同“分布式数据库”的横向分析,本文我将讲述拆解的第二部分,会结合NoSQL与NewSQL的差异,从纵向来谈谈OLTP场景“分布式数据库”实现方案的关键技术要点。这样的思考是前文的延伸,也是分布式数据库专题文章的一个总纲,其中的要点我之后也会单独撰文阐述。
一、NewSQL & NoSQL
NewSQL是本专题关注的重点,也是前文中特指的“分布式数据库”,其适用于OLTP场景,具有高并发低延迟的特点,特性接近Oracle/DB2等传统数据库,依赖通用X86服务器实现性能上的水平拓展,能够扛住海量交易的性能压力。
目前具有较高知名度的NewSQL有Google的Spanner / F1、阿里的OceanBase、CockroachDB、TiDB。其中后两者是正在成长中的开源项目,2018年相继发布了2.0版本。
NewSQL与NoSQL有很深的渊源,所以下文在对NewSQL的介绍中会穿插一些NoSQL对应的实现技术方式。
1、存储引擎
B+ Tree
B+树是关系型数据库常用的索引存储模型,能够支持高效的范围扫描,叶节点相关链接并且按主键有序,扫描时避免了耗时的遍历树操作。B+树的局限在于不适合大量随机写场景,会出现“写放大”和“存储碎片”。
以下借用姜承尧老师书中的例子[1]来说明B+树的操作过程
存在高度为2的B+树,存储在5个页表中,每页可存放4条记录,扇出为5。下图展示了该B+ Tree的构造,其中略去了叶子节点指向数据的指针以及叶子节点之间的顺序指针:
B+树由内节点(InterNode)和叶节点(LeafNode)两类节点构成,后者携带指向数据的指针,而前者仅包含索引信息。
当插入一个索引值为70的记录,由于对应页表的记录已满,需要对B+树重新排列,变更其父节点所在页表的记录,并调整相邻页表的记录。完成重新分布后的效果如下:
变更过程中存在两个问题:
本例中,逻辑上仅需要一条写入记录(黄色标注),实际变动了3个页表中的7条索引记录,额外的6条记录(绿色标注)是为了维护B+树结构产生的写放大。
注:写放大(Write Amplification):Write amplification is the amount of data written to storage compared to the amount of data that the application wrote,也就是说实际写入磁盘的数据大小和应用程序要求写入数据大小之比
新增叶节点会加入到原有叶节点构成的有序链表中,整体在逻辑上是连续的;但磁盘存储上,新增页表申请的存储空间与原有页表很可能是不相邻的。这样,在后续包含新增叶节点的查询中,将会出现多段连续读取,磁盘寻址的时间将会增加。进一步来说,在B+Tree上进行大量随机写会造成存储的碎片化。
在实际应用B+Tree的数据库产品(如MySQL)中,通常提供了填充因子(Factor Fill)进行针对性的优化。填充因子设置过小会造成页表数量膨胀,增大对磁盘的扫描范围,降低查询性能;设置过大则会在数据插入时出现写扩大,产生大量的分页,降低插入性能,同时由于数据存储不连续,也降低了查询性能。
LSM-Tree
LSM-Tree(Log Structured-Merge Tree)由Patrick O'Neil首先提出,其在论文[2]中系统阐述了与B+树的差异。而后Google在Bigtable中引入了该模型,如下图所示:
LSM-Tree的主要思想是借助内存将随机写转换为顺序写,提升了写入性能;同时由于大幅度降低了写操作对磁盘的占用,使读操作获得更多的磁盘控制权,读操作性能也并未受到过多的影响。
写操作简化过程如下:
该模型规避了随机写的IO效率问题,有效缓解了B树索引的写放大问题,极大的提升了写入效率。
NoSQL广泛使用了LSM-Tree模型,包括HBase、Cassandra、LevelDB、RocksDB等K/V存储。
当然LSM-Tree也存在自身的缺陷:
注释:
Major Compaction操作的意义是降低读操作的时间复杂度。设系统包含多个SSTable文件,共有N数据,SSTable平均包含m数据。
执行读操作时,对单一SSTable文件采用折半查找方法的时间复杂度为O(log2m),则整体时间复杂度为O(N/m* log2m);合并为一个SSTable后,时间复杂度可降低到O(log2N)
Leveled LSM Tree
Leveled LSM Tree 的变化在于将SSTable做了进一步的分层,降低写放大的情况,缩小了读取的文件范围,在LevelDB 中率先使用,随后Cassandra 1.0也引入了该策略[3]。
SSTable的层次化设计策略是:
Level间Compact操作
多个有序的SSTable,避免了Major Compaction这样的重量级文件重写,每次仅更新部分内容,降低了写放大率。
对于读取元数据来锁定相关的SSTable,效率显然超过了对所有SSTable的折半查找和Bloom Filter。因此,读取效率得到了显著提升,按照某种评估方式[3],在每行数据大小基本相同的情况下,90%的读操作仅会访问一个SSTable。
该策略下,Compaction的操作更加频繁,带来了更多I/O开销,对写密集型操作而言,最终结果是否能够得到足够的效率提升存在不确定性,需要在应用中权衡。
NewSQL的策略
NewSQL数据库的存储层普遍都采用K/V存储,所以基本沿用了LSM Tree模型。其中CockroachDB和TiDB都在KV层使用RocksDB。OceanBase采用了不同的方法规避Major Compaction的影响,大体是利用闲置的副本(Follower)进行Compaction操作,避免了对读操作的阻塞,在Compaction完成后,进行副本的角色的替换。
同时,K/V存储引擎仍然在继续发展中,一些其他的改进如分形树(Fractal Tree)等,限于篇幅我们就不在此展开了。
2、分片
分片(Sharding)概念与RDBMS的分区相近,是分布式数据库或分布式存储系统的最关键特性,是实现水平扩展的基础,也在NoSQL类系统中得到了大量运用。
分片的目标是将数据尽量均匀地分布在多个节点上,利用多节点的数据存储及处理能力提升数据库整体性能。
Range&Hash
虽然不同的系统中对分片策略有很多细分,但大致可以归纳为两种方式,Range和Hash。
Range分片有利于范围查询,而Hash分片更容易做到数据均衡分布。在实际应用中,Range分片似乎使用得更多,但也有很多应用会混合了两种分片方式。
HBase采用了Range方式,根据Rowkey的字典序排列,当超过单个Region的上限后分裂为两个新的Region。Range的优点是数据位置接近,在访问数据时,范围查找的成本低;缺点也比较明显,在容易出现热点集中的问题。
例如,在HBase通常不建议使用业务流水号作为RowKey,因为连续递增的顺序号在多数时间内都会被分配到同一个RegionServer,造成并发访问同时竞争这个RegionServer资源的情况。为了避免该问题,会建议将RowKey进行编码,序号反转或加盐等方式。这种方式实质上是使用应用层的设计策略,将Range分片转换成类似Hash分片的方式。
Spanner的底层存储沿用了BigTable的很多设计思路,但在分片上有所调整,在Tablet内增加了Directory的动态调配来规避Range分片与操作热点不匹配的问题,后续在事务管理部分再详细描述。
静态分片&动态分片
按照分片的产生策略可以分为静态分片和动态分片两类。
静态分片在系统建设之初已经决定分片的数量,后期再改动代价很大;动态分片是指根据数据的情况指定的分片策略,其变更成本较低,可以按需调整。
传统的DB + Proxy方案,进行水平分库分表就是一种常见的静态分片。我们熟知的几个互联网大厂在大规模交易系统中都进行过类似的设计,默认将数据做成某个固定数量的分片,比如100、255、1024,或者其它你喜欢的数字。分片的数量可以根据系统预期目标的整体服务能力、数据量和单节点容量进行评估,当然具体到100片合适还是1024片合适,多少还是有拍脑袋的成分。
在NoSQL中,Redis集群也采用同样的静态分片方式,默认为16384个哈希槽位(等同于分片)。
静态分片的缺点是分片数量已经被确定,基于单点处理能力形成一个容量的上限;灵活性较差,后续再做分片数量调整时,数据迁移困难,实现复杂。优点也很明显,静态分片的策略几乎是固化的,因此对分区键、分区策略等元数据管理的依赖度很低,而这些元数据往往会形成分布式数据库中的单点,成为提升可靠性、可用性的障碍。
相比之下,动态分片的灵活性更好,适用于更丰富的应用场景,所以NewSQL也主要采用动态分片方式,代价则是对元数据管理的复杂度增加。
在分片处理上,NoSQL与NewSQL面临的问题非常接近。
3、副本
首先是由于通用设备单机可靠性低,必须要通过多机副本的方式。本文中关注两个问题:一是副本一致性;二是副本可靠性与副本可用性的差异。
数据副本一致性
多副本必然引入了副本的数据一致性问题。之前已经有大名鼎鼎的CAP理论,相信大家都是耳熟能详了,但这里要再啰嗦一句,CAP里的一致性和事务管理中的一致性不是一回事。我遇到过很多同学有误解,用CAP为依据来证明分布式架构不可能做到事务的强一致性,而只能是最终一致性。
事务的一致性是指不同数据实体在同一事务中一起变更,要么全部成功,要么全部失败;而CAP中的一致性是指原子粒度的数据副本如何保证一致性,多副本在逻辑上是同一数据实体。
副本同步大致归纳为以下三种模式:
副本可靠性与副本可用性
数据副本仅保证了数据的持久性,即数据不丢失。我们还面临着副本的可用性问题,即数据是否持续提供服务。以HBASE-10070为例来说明这个问题:
HBase通过分布式文件系统HDFS实现了数据多副本的存储,但是在提供服务时,客户端是连接到RegionServer进而访问HDFS上的数据。因为一个Region会被唯一的RegionServer管理,所以RegionServer仍然是个单点。
在RegionServer宕机时,需要在一定的间隔后才被HMaster感知,后者再调度起一个新的RegionServer并加载相应的Region,整个过程可能达到几十秒。在大规模集群中,单点故障是频繁出现的,每个单点带来几十秒的局部服务中断,大大降低了HBase的可用性。
为了解决这问题,HBase引入从RegionServer节点的概念,在主节点宕机时,从节点持续提供服务。而RegionServer并不是无状态服务,在内存中存储数据,又出现了主从RegionServer间的数据同步问题。
HBase实现了数据的可靠性,但仍不能充分实现数据的可用性。CockroachDB和TiDB的思路是实现一个支持Raft的分布式KV存储,这样完全忽略单节点上内存数据和磁盘数据的差异,确保数据的可用性。
4、事务管理
分布式事务处理由于其复杂性,是NoSQL发展中最先被舍弃的特性。但由于大规模互联网应用广泛出现,其现实意义逐渐突出,又重新成为NewSQL无法规避的问题。随着NewSQL对事务处理的完善,也让过去十余年数据库技术的演进终于实现了一个接近完整的上升螺旋。
鉴于分布式事务管理的复杂性,我在本文中仅作简要说明,后续文章中会进一步展开。
NewSQL事务管理从控制手段上分为锁(Lock-Base)和无锁(Lock-Free)两种,其中,无锁模式通常是基于时间戳协调事务的冲突。从资源占用方式上,分为乐观协议和悲观协议,两者区别在于对资源冲突的预期不同:悲观协议认为冲突是频繁的,所以会尽早抢占资源,保证事务的顺利完成;乐观协议认为冲突是偶发的,只在可以容忍的最晚时间才会抢占资源。
以下通过最经典的“两阶段提交协议”和具体的两种应用实践,来具体阐述实现方式:
两阶段提交协议(2PC)
两阶段提交协议(Tow phase commit protocol,2PC)是经典的分布式事务处理模型,处理过程分为两个阶段:
请求阶段:
提交阶段:
该模型的主要优点是原理简单,实现方便。
缺点也很明显,首先是同步阻塞,整个事务过程中所有参与者都被锁定,必然大幅度影响并发性能;其次是单点问题,协调者是一个单点,如果在第二阶段宕机,参与者将一直锁定。
Spanner
根据Spanner论文[4]的介绍,其分布式事务管理仍采用了2PC的方式,但创新性的设计是改变了Tablet的数据分布策略,Tablet不再是单一的连续Key数据结构,新增了Directory作为最小可调度的数据组织单元。
通过动态的调配,降低事务内数据跨节点分布的概率。
我将这种事务处理的设计思想理解为:“最好的分布式事务处理方式,就是不做分布式事务处理,将其变为本地事务”。在OceanBase的早期版本中也采用了一个独立的服务器UpdateServer来集中处理事务操作,理念有相近之处。
Percolator
Percolator[5]是Google开发的增量处理网页索引系统,在其诞生前,Google采用MapReduce进行全量的网页索引处理。这样一次处理的时间取决于存量网页的数量,耗时很长;而且即使某天只有少量的网页变更,同样要执行全量的索引处理,浪费了大量的资源与时间。采用Percolator的增量处理方式后,大幅度减少了处理时间。
在这篇论文中给出了一个分布式事务模型,是“两阶段提交协议”的变形,其将第二阶段的工作简化到极致,大幅提升了处理效率。
具体实现上,Percolator是基于BigTable实现分布式事务管理,通过MVCC和锁的两种机制结合,事务内所有要操作的记录均为新增版本而不更新现有版本。这样做的好处是在整个事务中不会阻塞读操作。
分布式事务管理的其他内容,包括无锁事务控制、全局时钟的必要性等等,待后续文章中再讨论。
二、结语
本文及其前文最初的立意是面向几类典型技术背景的同学,对“分布式数据库”展开不同方向的解读,并就其中部分技术要点进行阐述,使不同技术领域的同学能够对相关技术有些许了解,为有兴趣深入研究的同学做一个铺垫。
随着分析的深入愈发觉得文章框架过于庞大难于驾驭,因而对关键技术的解读也存在深浅不一的情况。对于本文中未及展开的部分,我会尽力在后续系列文章中予以补充,水平所限文中必有错漏之处,欢迎大家讨论和指正。
文献参考:
[1] 姜承尧, MySQL技术内幕:InnoDB存储引擎机, 械工业出版社, 2011
[2] Patrick O'Neil The Log-Structured Merge-Tree
[3] Leveled Compaction in Apache Cassandra
https://www.datastax.com/dev/blog/leveled-compaction-in-apache-cassandra
[4] James C. Corbett, Jeffrey Dean, Michael Epstein, et al. Spanner: Google's Globally-Distributed Database
[5] Daniel Peng and Frank Dabek, Large-scale Incremental Processing Using Distributed Transactions and Notifications
网站标题:从NoSQL到NewSQL,谈交易型分布式数据库建设要点
网页URL:http://www.shufengxianlan.com/qtweb/news12/527512.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联