大规模的业务应用下,单体数据库遇到的主要问题首先是写性能不足,,另外还有存储方面的限制。而分片就是解决性能和存储这两个问题的关键设计,甚至不仅是分布式数据库,在所有分布式存储系统中,分片这种设计都是广泛存在的。

1 什么是分片

在不同系统中有别名:

  • Spanner和YugabyteDB称Tablet
  • HBase和TiDB称Region
  • CockraochDB称Range

分片是一种水平切分数据表的方式,数据记录的集合,也是数据表的组成单位。

分布式数据库的分片与单体数据库分区相似:

  • 分区虽可将数据表按策略切分成多个数据文件,但这些文件仍存储在单节点
  • 分片则可进一步根据特定规则,将切分好的文件分布到多个节点,实现更强大的存储和计算能力

分片机制通常关注:

1.1 分片策略

主要Hash(哈希)、Range(范围)两种。Key和List可看作Hash和Range的特殊情况。

1.2 分片的调度机制

  • 静态,分片在节点上的分布基本固定,移动也需人工介入
  • 动态,通过调度管理器基于算法在各节点间自动移动分片

和架构风格对应:

分布式数据库的分片机制_数据

PGXC只支持静态的Hash分片和Range分片,实现机制较为简单,所以,我就从这里开始展开吧。

2 PGXC

2.1 Hash分片

按数据记录中指定关键字的Hash值,将数据记录映射到不同分片:

分布式数据库的分片机制_主键_02

表格显示了社交网站记录表,包括主键、用户ID、分享内容和分享时间等字段。假设用户ID作关键字分片,系统通过Hash函数计算用户ID的Hash值后取模,分配到对应分片。模为4,因为系统共四4个节点,每个节点作一个分片。

Hash计算会过滤掉数据原有业务特性,可保证数据均匀分布到多分片,这是Hash分片最大优势,且实现简洁。但示例分片方法直接用节点数作为模,如系统节点数量变动,模也变,数据就要重新Hash计算,导致大规模数据迁移。这对扩展性很不友好。

2.2 一致性Hash

如何提升系统扩展性?一致性Hash,该算法首次提出在论文“Consistent Hashing and Random Trees : Distributed Caching Protocols for Relieving Hot Spots on the World Wide Web”。

要在工业实践中应用一致性Hash算法,首先会引入虚拟节点,每个虚拟节点就是一个分片。案例分片数量设定为16。但因分片数量决定集群的最大规模,所以它通常远大于初始集群节点数。

分布式数据库的分片机制_数据_03

16个分片构成整个Hash空间,数据记录的主键和节点都要通过Hash函数映射到这个空间。这Hash空间是个Hash环:

分布式数据库的分片机制_主键_04

节点和数据都通过Hash函数映射到Hash环,数据按照顺时针找到最近节点。

新增一台服务器,即节点E时,受影响的数据仅仅是新服务器到其环空间中前一台服务器(即沿着逆时针方向的第一台服务器)之间数据。结合我们的示例,只有小红分享的消息从节点B被移动到节点E,其他节点的数据保持不变。此后,节点B只存储Hash值6和7的消息,节点E存储Hash值4和5的消息。

分布式数据库的分片机制_数据_05

Hash函数优点:数据可较均匀分配到各节点,并发写入性能更好。

本质上,Hash分片是静态分片,须在设计之初约定分片最大规模。因Hash函数已过滤业务属性,很难解决访问业务热点问题。

业务热点:由于局部的业务活跃度较高,形成系统访问热点。如:

  • 电商网站的某热销商品
  • 或外卖网站的某店接单较多
  • 或某个银行网点的客户业务量较大

2.3 Range静态分片

与Hash分片不同,Range分片的特点恰恰是能够加入对于业务的预估。例如,我们用“Location”作为关键字进行分片时,不是以统一的行政级别为标准。因为注册地在北京、上海的用户更多,所以这两个区域可以按照区县设置分片,而海外用户较少,可以按国家设置为分片。这样,分片间的数据更加平衡。

分布式数据库的分片机制_Group_06

但是,这种方式依然是静态的,如果海外业务迅速增长,服务海外用户的分片将承担更大的压力,可能导致性能下降,用户体验不佳。

相对Hash分片,Range分片的适用范围更加广泛。其中一个非常重要的原因是,Range分片可以更高效地扫描数据记录,而Hash分片由于数据被打散,扫描操作的I/O开销更大。但是,PGXC的Range分片受限于单体数据库的实现机制,很难随数据变动和负载变化而调整。

虽然有些PGXC同时支持两种分片方式,但Hash分片仍是主流,比如GoldenDB默认使用Hash分片,而TBase仅支持Hash分片。

3 NewSQL

总体上,NewSQL也是支持Hash和Range两种分片方式的。具体就产品来说,CockroachDB和YugabyteDB同时支持两种方式,TiDB仅支持Range分片。

NewSQL数据库的Hash分片也是静态的,所以与PGXC差别不大,这里就不再赘述了。接下来,我们重点学习下Range动态分片。

3.1 Range动态分片

NewSQL的Range分片,多数是用主键作为关键字来分片的,当然主键可以是系统自动生成的,也可以是用户指定的。既然提供了用户指定主键的方式,那么理论上可以通过设定主键的产生规则,控制数据流向哪个分片。但是,主键必须保证唯一性,甚至是单调递增的,导致这种控制就会比较复杂,使用成本较高。所以,我们基本可以认为,分片是一个系统自动处理的过程,用户是感知不到的。这样做的好处显然是提升了系统的易用性。

将NewSQL的Range分片称为动态分片,因为:

① 分片可以自动完成分裂与合并

当单个分片的数据量超过设定值时,分片可以一分为二,这样就可以保证每个分片的数据量较为均衡。多个数据量较少的分片,会在一定的周期内被合并为一个分片。

社交网站,根据消息数量自动分片,可得R1、R2、R3三个分片:

分布式数据库的分片机制_Group_07

分片也会被均衡地调度到各节点,节点间的数据量也保持总体平衡。

② 可根据访问压力调度分片

系统之所以尽量维持分片之间及节点间的数据量均衡,存储的原因外,还更大概率将访问压力分散到各节点。但有少量数据可能成访问热点。如琦琦和静静都是娱乐明星,有很多粉丝关注她们分享的内容,其访问量远超过普通人。这时候,系统会根据负载情况,将R2、R3分别调度到不同节点均衡访问压力。

存储均衡访问压力均衡,是NewSQL分片调度机制普遍具备的两项能力。还有两项能力在Spanner论文,但其他产品没看到工程实现。

③ 减少分布式事务

分布式事务开销永远不会小于单节点本地事务开销。因此,所有分布式数据库都试图通过减少分布式事务提升性能。

Spanner在Tablet即Range分片下增加目录(Directory),作为数据调度最小单位,调度范围可跨Tablet。通过调度Directory可将频繁参与同样事务的数据,转移到同一Tablet下,从而将分布式事务转换为本地事务。

④ 缩短服务延时

全球化部署的分布式数据库,数据可能存储在相距很远的多个IDC,如用户需访问远端机房的数据,操作延时较长,这受制于数据传输速度。而Spanner可以将Directory调度到靠近用户的IDC,缩短数据传输时间。这里的调度对象都是数据的主副本,跨中心的数据副本仍存在,负责保证系统整体的高可靠性。

Directory虽然带来新的特性,但也削弱分片的原有功能,分片内的记录不再连续,扫描要付出更大成本。

  • 减少分布式事务
  • 靠近客户端位置

不能兼顾,再加上存储和访问压力,分片调度机制要在四个目标间进行更复杂的权衡。

Spanner这种设计能达到啥实际效果还要继续观察。

4 分片与高可靠

高可靠是分布式数据库的重要特性,分片是数据记录的最小组织单位,也必须是高可靠的。

NewSQL与PGXC的区别在于,对于NewSQL来说,分片是高可靠的最小单元;而对于PGXC,分片的高可靠要依附于节点的高可靠。

NewSQL的实现方式是复制组(Group)。在产品层面,通常由一个主副本和若干个副本组成,通过Raft或Paxos等共识算法完成数据同步,称为Raft Group或Paxos Group,所以我们简称这种方式为Group。因为不相关的数据记录会被并发操作,所以同一时刻有多个Group在工作。因此,NewSQL通常支持Multi Raft Group或者Multi Paxos Group。这里,我们先忽略Multi Paxos的另一个意思。

每个Group是独立运行的,只是共享相同的网络和节点资源,所以不同复制组的主副本是可以分布在不同节点的。

PGXC的最小高可靠单元由一个主节点和多个备节点组成,我们借用TDSQL中的术语,将其称为Set。一个PGXC是由多个Set组成。Set的主备节点间复制,多数采用半同步复制,平衡可靠性和性能。这意味着,所有分片的主副本必须运行在Set的主节点上。

从架构设计角度看,Group比Set更具优势,原因主要有两个方面。首先,Group的高可靠单元更小,出现故障时影响的范围就更小,系统整体的可靠性就更高。其次,在主机房范围内,Group的主副本可以在所有节点上运行,资源可以得到最大化使用,而Set模式下,占大多数的备节点是不提供有效服务的,资源白白浪费掉。

5 总结

  1. 分片是分布式数据库的关键设计,以此实现多节点的存储和访问能力。
  2. 分片机制的两个要点是分片策略和调度机制,分片策略包括Hash和Range两种,调度机制则分为静态和动态。
  3. PGXC使用单体数据库作为数据节点,往往只实现了静态分片。它的分片策略支持Hash和Range两种,其中Hash一般是指一致性Hash,可以最大程度规避节点扩缩带来的影响。Hash分片写性能出众,但查询性能差,Range则相反。
  4. NewSQL的默认分片策略通常是Range分片。分片调度机制为了实现存储平衡和访问压力平衡的目标,会将分片动态调度到各个节点。Spanner的设计又将在分片下拓展了Directory,通过对Directory的调度实现减少分布式事务和缩短延时的目标,但在其他分布式数据库中尚未看到对应的实现。
  5. NewSQL架构下,分片采用Paxos或Raft算法可以构成复制组,这种复制机制相比PGXC的主备节点复制,提供了更高的可靠性,资源使用也更加高效。

Range是更好分片策略,因为Range分片有条件做到更好的动态调度,只有动态才能自适应各业务场景下数据变化,平衡存储、访问压力、分布式事务和访问链路延时等诉求。NewSQL的Range分片方式更加优雅,随单体数据库底层数据同步机制的改进,未来PGXC可能也这样。

深入了解Range分片机制,研究BigTable的论文。HBase是业界公认的BigTable开源实现,官方文档

分布式数据库的分片机制_主键_08

参考

6 FAQ

Range分片优势动态调度,即分片存储在哪个节点上是不断变化的。这时,客户端先要知道分片位置,就要先访问分片的元数据。这些元数据如何存储?存储在某中心点,还是分散在所有节点?如果有多个副本,又该如何同步呢?

如是TiDB,将元数据存在PD,而PD本身又可部署为多节点高可用的,不过数据最终落在etcd,PD只是交互节点。 Spanner如何做的就不太好猜测,但是Spanner也有PD这个角色,也许是差不多的。TiDB还做了一些优化。

Hash 分片写性能出众,但查询性能差,Range 则相反。文中哪里有详细阐释为什么Hash分片的写性能更好呢?为什么Range的写性能就不行?

  • Hash分片会将数据比较均匀的分散在集群的各节点,所以性能更好
  • Range数据分布是根据编码规则(静态)或主键(动态),不以追求平均分布为目标,所以性能稍差

多数采用半同步复制,平衡可靠性和性能。这意味着,所有分片的主副本必须运行在 Set 的主节点上。为什么使用半同步复制,所有分片的主福本都运行在set主节点?

这是由单体数据库的主从复制机制决定的,无论哪种策略,都是以节点为单位的,从节点不能提供确保数据一致性的服务。

PGXC的这种模式,如果按Set来分片的话,那么为什么不能像Multi Raft Group一样,主Set副本分布在不同节点呢?这样就可以把读写压力分摊在不同节点上了?

对PGXC的分片模式和Multi Raft Group模式的比较,我的理解是:

PGXC采用的是共享磁盘架构,数据按Set分片,每个Set中的数据存储在共享的存储设备上。Set的主节点和备节点都是访问同一份数据。这种模式的优点是数据同步和故障转移很简单,主备节点直接访问同一份数据即可。

而Multi Raft Group是将同一个逻辑分片的数据副本分布在不同的物理节点上。读写需要在不同节点间协调和同步。这种模式可以提高可用性,如果个别节点故障,其他节点的数据副本还可以继续提供服务。但是实现更加复杂,需要处理节点间的数据同步、一致性等问题。

PGXC不采用Multi Raft Group的模式主要出于以下考虑:

  1. PGXC的共享磁盘架构本身就可以提供较高的数据可用性,单个节点故障不会导致数据不可用。
  2. Set的数据副本分布在不同节点会增加数据同步和一致性的难度,不太适合PGXC的场景。
  3. Set的数据副本在同一存储设备上,可以很方便地进行主备切换,实现高可用。
  4. Set的主备节点同时处理请求,可以达到读写分离的效果,一定程度上可以分摊压力。

所以PGXC当前的模式比Multi Raft Group更简单、易管理,也能满足大部分场景下的高可用需求。除非对强一致性、高可用性要求极高,现有的模式也比较符合实际需要。

大部分分布式系统都有这么一个存储元数据的东西,比如TiDB的PD,HBase里的ZK,k8s的etcd。也可以把他们看成存储小数据的KV存储系统,一般通过Raft或者Paxos来维持共识,就跟普通分布式系统一样。

mongodb的hash分片

还是挺巧妙的,他的算法依赖的应该不是节点数,而是跟数据的chunk数相关,这样增减节点,只是涉及chunk的移动,不会大面积做全部数据的重平衡。当然算法如果仅仅只是chunk数相关,那chunk数变化就会触发算法变化,所以应该是做了优化的,保证chunk的分裂只会影响分裂chunk的数据移动。

元数据集中存储,特别是能用全内存性能最好,但可靠性不足,一般做HA;或元数据可以做一致性Hash来分片打散,个人认为Range不适合元数据,变化了数据位置不好计算。

Hash分片确实是无主架构常采用的方式,但CockroachDB的Range分片也是一个不错的思路。

es的,ceph的分片机制也是类似,ceph有自动rebalance,es貌似要手动。