分布式数据库选型——数据水平拆分方案

概述

水平拆分的概念随着分布式数据库的推广已为大部分人熟知。分库分表、异构索引、小表广播、这些功能几乎是产品功能需求标配。然而有些客户使用分布式数据库后的体验不尽如意。
本文尝试从数据的角度总结分布式数据的复制(replication)和分区(partition)技术原理和方案,其中分区也有称为分片(sharding),但愿能引发读者一些思考,在分布式数据库选型中能注意这些细节的区别,选择适合业务的数据水平拆分方案。算法

分布式数据库架构

分布式数据库以集群形式存在,有多个节点。集群架构有共享磁盘架构(shared-disk)和无共享架构(shared-nothing)。后者有时也称为水平扩展(horizontal scale)或向外扩展(scale out),本文主要总结无共享架构方案。数据库

无共享架构的各个节点之间的通讯都是软件层面使用网络实现,不一样产品在架构不一样致使这个细节也不一样。有些架构是计算与存储分离。计算节点特色是无状态(即数据不要求持久化),经过集群方式管理,能够水平扩展;存储节点有数据,使用复制和分区技术,节点间任务集中调度或者独立交互。了解这个架构细节均可用性分析会更加具体。具体分布式数据库架构有哪些请参考《一些关系数据库的架构总结》。网络

这里节点的实际体现形式能够是一个机器,也能够是机器上的一个实例。好比说有些数据库支持单机安装多个实例,如MySQL。每一个节点具有必定的资源和能力。资源指的是CPU、内存和磁盘,能力是提供数据读写和存储能力。分布式数据库须要把多个节点的能力汇集到一块儿集中管理,只是不一样分布式数据库产品对资源的管理能力各有特色。架构

在分布式数据库里,数据随处可见,这是最容易让人混淆的地方。由于数据通过复制和分区后会有两种存在形式:副本(replica)和分区(partition)。并发

数据的复制(replication)

复制(replication)指在几个不一样的节点上保存数据的相同副本(replica)。复制提供了冗余的能力。其做用一是提供高可用能力:若是一个节点不可用,剩余的节点能够快速提供数据服务。做用二是提供读写分离能力。常见的有两副本和三副本架构。负载均衡

多个副本内容相同,角色会有区分。常见的是一个副本是Leader角色(有的也称主副本),默认提供读写服务;其余副本是Follower角色(有的也称备副本),默认不提供服务。这种架构也称为基于单Leader的(Single Leader-based)。还有其余架构是多Leader的,每一个Leader都有数据要复制到其余Leader或Follower,这种架构会有个明显的问题就是数据冲突处理。若是产品层面不处理,用户直接使用风险会很高。运维

后面讨论的是前者:基于单Leader副本架构。异步

多副本之间数据同步不是依赖业务多写,而是采用副本间复制事务日志(Redo)技术。复制的方式有同步复制和异步复制。使用同步复制方式,备副本要收到Redo并落盘主副本才能提交,也叫强同步;使用异步复制方式,Follower副本相对Leader副本内容会有延时,具体延时多少取决于Leader副本上事务量、网络传输速度、Follower副本所在节点的负载和能力。强同步的缺点时主副本写性能会降低,同时若是备副本不可用主副本也不能提供服务(变相的解决方案是复制方式降级为异步复制)。分布式

传统关系型数据库还有一种用法一主两备架构,使用同步复制,只要任何一个备副本收到Redo,主副本的事务就能够提交。这个方案优势是保障了数据在多个副本中存在,高可用时有候选副本,也不用担忧挂掉一个备副本会影响主副本。它的缺点是不能自动知道哪一个候选副本拥有主副本最新最全的数据,也不强制要求两个备副本都要拥有所有数据。函数

还有一类三副本架构在复制时使用的是Paxos协议,三副本会就Redo落盘事件进行投票,有两个副本成功了Leader副本的事务便可提交。这个表面上跟上面传统一主两备的三副本效果同样,实际上仍是有区别的。区别一是使用Paxos协议时,若是Leader副本自身投票慢了,两个Follower副本投票成功,Leader副本的事务也是能提交的;区别二是第三个副本最终也必须写Redo成功,不然其状态就是异常,产品自身能够发现并自动修复(如从新建立一个副本);区别三是使用Paxos协议时,在Leader副本不可用时还能够自动选出新的Leader副本而且拥有老Leader副本的最新数据。这里其实说的是高可用机制。一样,这里对用户而言也不知道哪一个Follower副本拥有最新最全的数据,若是访问Follower副本(读写分离),也可能发现数据有延时。

大部分数据库作副本复制使用的是Redo,也称为物理同步。在应用Redo的时候直接是数据块变动。使用物理同步机制的备副本是不提供写服务,不能修改。还有一类复制使用的是Binlog,也称为逻辑同步。Binlog里只包含已提交的事务,而且在应用的时候是经过执行SQL。使用逻辑同步的备副本一般也多是主副本,能够修改(如MySQL的双向复制架构Master-Master)。若是目标端数据不对,应用SQL会失败,这个复制就会中断须要人介入处理。这也进一步加深了主备副本不一致的几率。

关于副本角色的粒度,有多种实现方案。

传统关系数据库主备架构,主副本或备副本的粒度就是实例。对于主实例(Primary)而言,里面全部数据库(或SCHEMA)的全部表的角色都是主;备实例(Standby)里数据则都是备副本。若是发生高可用切换,业务会中断几十秒或几分钟而后恢复(须要人工处理或自动化脚本处理)。

还有一种粒度是到表。即一个节点内有些表是Leader副本,有些表是Follower副本,这样这个节点就不能简单的说是主节点(实例)或备节点(实例)。这个副本角色细节业务也是能够获取的,若是发生高可用切换,业务会中断十几秒而后恢复。

还有一种粒度是存储级别的定长块。即一个节点的存储里,部分数据块是Leader副本,部分数据块是Follower副本。这种对业务就彻底透明,业务基本不感知高可用切换。

数据的分区(partition)

上面总结的是数据的复制(冗余,多副本),对于很是大的数据集(表)或者很是高的访问量(QPS),仅仅靠复制是不够的,还须要对数据进行分区(partition),也称为分片(sharding)。

分区粒度

首先这里的分区(partition)是一种抽象概念,在不一样数据库产品里这个体现是不同的。如在MongoDB, Elasticsearch中体现为分片(shard),在HBase中体现为区域块(Region),Bigtable中体现为表块(tablet),ORACLE中体现为分区(partition),Couchbase中体现为虚拟桶(vBucket)。可见不一样的数据库产品数据分区的粒度不一样。在分布式关系数据库中间件中,分片的粒度是分表(物理表);在真正的分布式关系数据库里,分片的粒度有分区(partition,同ORACLE)或者区域块(Region)。

分区粒度对业务研发的使用体验影响很大。

好比说中间件经常使用分库分表方案,使用时对开发和运维会有一些要求。如建不少同构的表并后期维护、要求SQL带上拆分键,还有一些功能限制(如跨库JOIN问题)、底层存储节点用的数据库自身高可用和多副本的数据一致问题等等。不一样的中间件产品能力上也有区别,互联网大厂的产品因为内部场景培育好久,作的相对成熟一些。

体验最好的分区粒度就是存储级别的Region,业务研发彻底不用关心分片细节,也没法干预分片细节。当有些场景追求性能须要干预数据分布特色时就很差处理。

介入这两种策略之间的就是分区。物理上业务只要建立一个分区表,根据业务特色指定分区策略(包含分区列、拆分算法、分区数目等)。

数据复制是为了冗余和高可用,数据分区主要是为了可扩展性。无论使用哪一种分区方案,业务的每条数据(记录)属于且仅属于一个分区(或分片sharding),同一个分区(分片)只会存在于一个节点。前面说了每一个节点表明了必定的资源和能力。当复制和分区(分片)一块儿使用的时候,注意区分你看到的数据。

分区策略

分区的目标是将大量数据和访问请求均匀分布在多个节点上。若是每一个节点均匀承担数据和请求,那么理论上10个节点就应该能承担10倍于单节点的数据量和访问量。这个理论是忽略了复制产生的Follower副本的存在。Follower副本的空间和内存是不可能跟其余Leader副本共享的,可是计算能力(CPU)是能够的。当全部节点都提供服务的时候(多活),是计算资源最大利用。

然而若是分区是不均匀的,一些分区的数据量或者请求量会相对比较高,出现数据偏斜(skew),这个可能致使节点资源利用率和负载也不均衡。偏斜集中的数据咱们又称为热点数据。避免热点数据的直接方法就是数据存储时随机分配(没有规则)给节点,缺点是读取的时候不知道去哪一个分区找该记录,只有扫描全部分区了,因此这个方法意义不大。实际经常使用的分区策略都是有必定的规则。

这个规则能够是业务规则,也能够不是。

业务规则的分区首先是选取一个或一组列做为分区键,而后选取拆分方法。好比说根据键的范围(Range)分区,分区数量和边界时肯定的(后期还能够新增分区)。好处时针对分区键的范围扫描性能会比较好。分布式数据库中间件的分库分表、分区表的分区都支持RANGE 拆分函数。各个产品拆分细节上面会有一些创新。Range分区的缺点是某些特定的访问模式会致使热点。好比说根据时间列作RANGE分区,业务写入和读写数据集中在最近的时间,就可能致使各个分区负载不均衡。这只是一个缺点,业务层面还要考虑这样作的好处。好比说删除历史分区比较快。

还有种拆分方法是散列(HASH)分区,分区数量和边界是肯定的(后期能够作分区分裂)。这时各个数据的分布是否均衡就取决于各个产品实现机制。大部分作法是使用一个散列(HASH)函数对Key计算一个值,而后针分段存储。

有的产品会使用这个HASH值对分区数取模,这个方法可能引发分区数据分布不均匀(若MySQL的Key分区)。此外若是要调整分区数,则须要移动全部数据。ORACLE的HASH分区时会先选取最接近分区数的一个2的幂值,对于分区数大于这个值的分区,会从前面分区里调过来。因此ORACLE 建议HASH分区数为2的幂。M有SQL建议Key分区数为奇数时数据分布最均匀。

此外在现有分区下还能够再作一次分区,分区键和分区方法均可以不同。一般称为两级分区。好比说分库分表时,分库和分表策略不同就是两级分区;分区表也支持两级分区。

有业务规则的分区方案的特色就是使用上。SQL若是要性能好建议带上分区键,这样分布式数据库才能够直接定位到所访问数据所在的分片;不然,数据库就要扫描全部分区去查询数据。一般分区键只能选取一个或一组业务字段,表明的是一个业务维度,那么另一种业务维度的SQL请求性能就会很差。个别分布式数据库产品在HASH 方法上支持两种维度的分区列,其前提是在业务构造数据时让这两个列有着内部一致的分区逻辑。

详情能够参考《说说分库分表的一个最佳实践》。

另一种分区策略就是无业务规则的,在存储级别按块的大小切分为多个定长块(Region)。这个分区对业务而言就是透明的,因此使用体验上会相对好一些。

不过,分布式数据库里的数据分区除了存储数据还要提供读写服务。业务读写数据的SQL自己是带业务逻辑的,若是一次SQL请求访问的数据分散到多个分区,而这些分区又散落在不一样的节点上,不可避免的会发生跨节点的请求。若是是多表链接,这种情形更容易出现。若是这个业务请求有事务,那这就产生了分布式事务。分布式事务解决方案有两种,强一致的两阶段提交(XA)方案和最终一致的TCC方案。详情请参考《说说数据库事务和开发(下)—— 分布式事务》。

这里主要提示跨节点的请求带来的性能衰减。固然,硬件方面万兆网卡加RDMA技术下网络延时已经缩小不少,可是当分布式数据库的请求量(QPS)很是高时,或者分布式数据库是多机房部署(好比说两地三中心)时,跨机房的网络延时仍是不可忽视,跨节点的请求带来的性能衰减也会很明显。因此有业务规则的分区策略能够提供策略给业务控制本身的数据分区分布特色,很是适合作异地多活和单元化类业务。此外还有个经常使用的规避跨节点请求读的方法就是小表广播,即将个别没有分区的表的数据复制到其余分区所在的节点,这样相关业务数据分区的JOIN就是在本地节点内部完成。这里就看复制使用的是物理同步仍是逻辑同步,以及同步的延时是否知足业务需求。

分区数量

关于分区数量也须要评估。若是是无规则的分区策略,因为每一个分区(分片)是定长块,那么分区数量就由总数据大小除以定长块大小,对业务也是透明的。这里总结的是有业务规则的分区的数量。

使用分区的目的是为了扩展性,具体就是能将不一样分区分散多多个节点上,发挥多个节点的资源和能力。因此分区数必定要大于可用的资源节点数,为了考虑到未来分布式数据库可能会扩容,分区数应该是数倍于当前规划的节点数。这是一个总的指导思想。因为不一样的分布式数据库其节点的表示方法不同,实施的时候会略有不一样。

好比说在分布式数据库中间件架构里,数据存储的节点是实例,数据分区的粒度是分表(物理表),中间还有一层分库的维度。分布式数据库实例:总物理实例数:总物理分库数:总物理分表数=1:M:N:X 。X是分区的数量,N 是总分库数。X 是固定的,若是要调整分区数,成本很是高,因此通常都是提早规划好。N 是总分库数,是2的幂。 M 是实例的数量,也建议是2的幂,决定了最大能用多少节点的资源。 N/M 的结果决定了将来能扩容的倍数。分布式数据库中间件因为数据分区落在具体的节点后就不能自由移动,其扩容方式可能是对每一个实例一分为二,最好的途径就是利用数据库(MySQL)自身的主从复制搭建新的备实例扩容节点数。

此外分区数还要考虑到单个分区的容量和请求量是否知足需求。即分区是否到位。这个也是须要业务评估的。在使用分区表的分区方案的分布式数据库里,分区数也是结合上面两点考虑的。

固然分区数太大了,可能会增长分布数据库内部管理成本。分区数量跟分区粒度刚好是相反关系,两者都须要取一个合适的值。

分区数量一旦肯定后,调整的成本很是高,一般会引发数据重分布。有些产品能够针对特定类型的分区作分区分裂。如RANGE分区能够分裂为两个RANGE, HASH分区也能够一分为二。只要这个分区分裂的逻辑是数据库内部逻辑实现,保证数据不丢,且对业务透明的,那么风险就很低值得考虑。

分区负载均衡

随着时间的推移,数据库一直在发生各类变化。如QPS增长,数据集更大,或者新增/替换机器等。不管哪一种都须要将部分数据分区和相应的请求从一个节点移动到另一个节点,这个过程称为分区的再平衡(rebalance)。业务对再平衡的要求就是平衡过程当中对业务当前读写影响要可控,数据读写服务不能中断。还有一点就是为了再平衡应尽量少的迁移数据。

前面两个要求都不难知足,最后一个要求就考验各个分区方案的灵活度了。当分区粒度是存储级别的Region时,分区迁移的粒度就是Region,这个对业务也是透明的;分区粒度是分区时,这个取决于各个产品对节点资源管理的设计。好比说有的设计能够作到只须要迁移分区就能够调整各个节点的资源利用率和负载;若是分区方案是分库分表,此时分区粒度是分表。可是数据迁移的单位一般仍是实例,利用数据库原生复制能力搭建新的级联备实例,而后新老实例分别删除一半分库数据。这里就迁移了没必要要的不少数据分区。

分区访问路由

如今数据分区方案已经肯定,业务数据分布在多个节点上。业务应用访问数据库如何链接呢?再分区负载均衡发生后部分分区节点发生变化,业务应用是否要修改链接?这个就是分区访问路由问题,是分布式数据库的基本能力。理论上分区访问路由有三种方案。一是每一个节点均可以进行路由转发(若是请求的数据不在该节点上,该节点能够转发应用请求到正确的节点上);二是设置一个中心模块负责接受请求并转发到正确的节点上;三是应用本身获取分布式数据库全部分区的节点信息,直接链接对应的节点,不须要其余组件提供路由功能。

大部分分布式数据库架构,选择了第二种方案,有一个负责分区路由访问的模块。有些产品同时支持这三种方案。 针对分区路由问题状况还可能更复杂。如一个事务有多条SQL时该路由到哪一个节点。此外就是若是负责路由的节点故障,或者分区所在节点故障,这个路由不可用或者失效时会如何恢复路由服务。

SQL线性扩展能力

当数据分区方案肯定、分区路由问题也解决了后,运维和业务架构为业务的搭建了一个好的分布式数据库环境。不少业务误觉得用上分布式数据库后,就必定会很好,或者扩容后业务的性能也能相应的提高。实际使用经验并不必定如此。仍是前面那句话使用分区方案主要是得到扩展性,其关键就是分区分布在更多的节点上,能利用上更多节点的能力。

但这个并非指让单个SQL利用更多节点的能力。举个例子在OLAP业务里,一条SQL 若是能让不少节点同时提供服务,其性能固然是最好的。不过这样的SQL的并发不能太多,不然很容易让全部节点都很忙。即便分布式数据库扩容了节点将分区进一步打散,因为业务的访问压力和数据量也会增长不少,极可能依然是每一个SQL同时让全部节点为其服务,这个SQL的吞吐量并不会随着这个节点数量的扩容而获得相应的提高。

分布式数据库的优点在于对于空间问题和请求访问问题分而治之。针对每一个分区的访问,由该分区所在的节点响应便可。即便该SQL 并发很高,因为访问的是不一样的分区,分别由不一样的节点提供服务。每一个节点自身也有必定能力知足必定的QPS,全部节点集中在一块儿就能提供更大的QPS。这个时候若是扩容节点数量,该SQL总的QPS也能得到相应的提高。这是分布式数据库里最好的情形。

第二个例子根据PK 访问表,而且PK仍是主键等。一般咱们都建议分库分表或者分区时,业务SQL尽可能带上拆分键就是这个道理。可是若是业务场景确实没法带上拆分键,除了强制扫描全部分区外,还有个解决方案就是全局索引表。全局索引是独立于数据分区存储的,全局索引能够避免扫描没必要要的分区,负面做用就是业务分区的写操做极可能带来分布式事务。

以上两个例子就是分布式数据库里SQL的先行扩展能力的两个极端。前一个场景SQL没有扩展能力,后一个SQL的扩展能力几乎是百分百。大部分SQL的先行扩展能力就界于二者之间。好比说SQL里是分区列的IN条件。这个SQL的先行扩展能力取决于这个INLIST的数据特色。若是刚好每次都是命中同一个分区,那跟分区列等值访问效果同样好;若是INLIST的数据命中绝大部分分区,那就接近OLAP 场景的那个SQL。有些业务增加后,这个INLIST的长度基本不变。好比说人口业务,虽然总人口的激增,但每一个家庭的子女数量大部分在1-2。这是一类特色,访问这个子女数据的SQL的先行扩展能力会很好。另一个例子就是买家订单查询业务。10年前每一个买家一段时间的订单数量可能就几个,现在每一个买家一段时间的平均订单数量可能在几十或几百。

比INLIST 更复杂的逻辑就是表链接。 表链接时的条件是不是分区列,每一个具体的链接值会相应命中多少个分区,是否有分布式执行计划等等。都会影响这个SQL的线性扩展能力。

对于无业务规则的分区方案,虽然分区对业务是透明的,但不能否认的是数据分区是分布在不一样的节点上,只要业务读写这些数据,数据分布特色就会影响到SQL的性能。对于业务而言,该如何选择?若是业务经过分区策略控制数据分区分布特色,可以得到更高的性能,业务是否愿意选择会影响分布式数据库的选型。而不一样分区方案在运维方面的特色也不同,是影响选型的另一个因素,这里就不细说。

蚂蚁的分布式数据库最佳实践

蚂蚁金服的业务规模很是大,业务模块划分很是细。以网商银行很是核心的交易、帐务和支付模块举例,每一个业务模块的数据经分布式数据库中间件(SOFA的DBP)拆分为多个OceanBase租户(实例)下百库百表,每一个表同时变动为OceanBase自身的分区表,分为100个分区。总共有多个OceanBase集群,每一个集群横跨杭州上海和深圳五机房,并同时提供服务。这里的数据总共分为10000个分区,不一样分库下的数据分区的Leader副本分别位于不一样的机房。不一样分表之间能够分别进行结构变动(灰度发布能力),不一样OceanBase租户甚至集群之间是物理隔离的,这是金融核心业务拆分有使用分库分表的第一个缘由。

业务层面数据是按用户维度拆分的,不一样的用户访问不一样的机房的应用和数据。业务层面的流量分配规则和数据分区Leader副本分配规则保持一致并联动,实现了任意时刻的在线业务流量机房间比例调整。这是拆分使用分库分表的第二个缘由。

OceanBase集群在蚂蚁金服业务里的核心做用是在数据库层面解决数据副本三地分布的强一致和高可用切换问题,而且提供了在线分区迁移和租户弹性伸缩能力。

后记

本文首先针对分布式数据库种的数据存在的两种形式副本(复制产生的)和分区(分区产生的)进行区分。而后总结了分区方案须要考虑的几个点:分区粒度、分区策略、分区迁移和负载均衡、分区数量和分区路由问题等。即便这些都考虑好了,也只是分布数据库这个初局作好了。后面业务可否发挥分布式数据库的优点就取决于业务SQL的写法是否有很好的线性扩展能力。最后简单总结了蚂蚁金服支付宝和网上银行在分布式数据库架构方面的最佳实践。


原文连接 本文为云栖社区原创内容,未经容许不得转载。

相关文章
相关标签/搜索