引言
分布式的优点在于系统的高可用、高性能和高可扩展。数据复制通过在不同节点保存数据的多个副本实现三高,而分布式数据中另一个基础技术数据分区通过将一个数据集拆分成多个较小的数据集,同时将存储和处理这些小数据集的任务分配给不同节点实现。
水平分区和垂直分区
分区可以分为垂直分区(Vertical Partitioning)和水平分区(Horizontal Partitioning)。这两种分区方式起源于关系型数据库,在设计数据库架构时十分常见。
垂直分区是对表的列进行拆分,将某些列的整列数据拆分到特定分区。垂直分区减少了表的宽度,每个分区都包含了其中的列对应的所有行。例如可以将不常使用的字段或包含 BLOB 类型的字段的所在列与其他列分离,在保证数据完整性的同时提高访问性能。
水平分区是对表的行进行拆分,将不同的行放入不同的表中,所有表中定义的字段在每个分区都能找到。例如一个包含十年订单的表可以拆分成十个分区,每个分区包含一年的记录。
在分布式系统中,水平分区也被称作分片(sharding),通常认为分片指数据分布在多个节点,而分区只是将单个存储文件拆分成多个小文件,但是在大多数语境下,两者基本意思相同。
Key-Value 数据的分区
分区的目标是将数据和查询负载均匀分布在所有节点上,理论上十个节点可以处理十倍的数据量和十倍于单个节点的读写吞吐量。但是就像并行计算一样,实际有可能出现某些节点承担更多数据量,称之为倾斜,分区算法即被设计用来避免倾斜问题,同时均匀分布数据。
范围分区
范围分区(Range Partitioning)是指根据指定的关键字将数据集拆分为若干范围。
如何划分范围可以由管理员设定,也可以由存储系统自行划分。通常会选择额外的负载均衡节点来接收请求,再根据范围分区算法重定向至响应节点。
因为每个分区内的数据可以按照关键字排序保存,所以范围分区可以很轻松地支持区间查询。同时也很容易通过修改范围边界来重新分区以实现负载均衡。
然而基于范围分区的缺点是可能会出现热点现象。如按年份拆分订单时,虽然数据分布均衡,但是基于生活习惯,最近一年订单的查询量可能比前几年加起来还要多,这时便会出现热点问题。
哈希分区
哈希分区(Hash Partitioning)指使用哈希函数计算指定的关键字的哈希值,再基于哈希值决定分区。
哈希分区的优点在于数据分布是随机的,分布相对均匀,因此可以一定程度上避免热点问题。
可以为每个分区分配一个哈希范围,分区的边界可以是均匀间隔,也可以是伪随机选择(一致性哈希)。
均匀间隔易于理解,但是当系统拓展或部分节点下线时,需要大范围移动数据,可能会非常耗时。
一致性哈希即用于缓解大规模数据移动问题。一致性哈希算法将哈希值抽象成一个首尾相连的圆环,然后将分布式节点映射到圆环上,再将数据的哈希值映射到圆环上,数据存储在顺时针方向遇到的第一个节点。
一致性哈希相比于普通的哈希分区,在添加或减少节点时,受影响的仅是在哈希环上相邻节点的数据。但是当节点过少时,还是容易出现数据分布不均的问题。当一个节点下线时,该节点的数据会完全倾斜到顺时针的下一个节点,造成负载不均。
对此,可以在哈希环上引入虚拟节点,虚拟节点是实际节点在哈希环上的副本,一个物理节点对应哈希环上的多个点。当某一节点下线时,可以将数据均匀分配给其余节点。
请求路由
完成数据分区后,还需要处理将客户端请求重定向至存储所需数据的节点上的路由问题。请求路由问题属于一种典型的服务发现问题,服务发现并不局限于数据库,任何需要通过网络访问的系统都有类似的需求,大概有如下几种策略:
- 允许客户端连接任意节点
- 增加一个中间件统一处理请求
- 客户端感知分区与节点的分配关系