键的分片
键值数据的分片
假设你有大量数据并且想要分区
如果分区是不公平的,一些分区比其他分区有更多的数据或查询,我们称之为偏斜(skew
避免热点最简单的方法是将记录随机分配给节点。这将在所有节点上平均分配数据,但是它有一个很大的缺点:当你试图读取一个特定的值时,你无法知道它在哪个节点上,所以你必须并行地查询所有的节点。
Range-based sharding,基于范围分片
基于范围的分片假定数据库系统中的所有键都可以排序,并且将键的连续部分作为分片单元。如果知道范围之间的边界,则可以轻松确定哪个分区包含某个值。如果您还知道分区所在的节点,那么可以直接向相应的节点发出请求(对于百科全书而言,就像从书架上选取正确的书籍

键的范围不一定均匀分布,因为数据也很可能不均匀分布。例如在上图中,第
分区边界可以由管理员手动选择,也可以由数据库自动选择,Bigtable,HBase,

如上图就是(minKey, maxKey)
。每个分片单元(块)都是连续键的一部分。基于范围的分片的优点是相邻数据在一起的可能性很高(例如具有公共前缀的数据
在每个分区中,我们可以按照一定的顺序保存键,好处是进行范围扫描非常简单,您可以将键作为联合索引来处理,以便在一次查询中获取多个相关记录。例如,假设我们有一个程序来存储传感器网络的数据,其中主键是测量的时间戳(年月日时分秒
但是,基于范围的分片对工作量大的顺序写入不友好。还是刚才的物联网例子中,在时间序列类型的写入负载的时候,写入热点始终位于最后一个区域中。发生这种情况是因为日志键通常与时间戳有关,并且时间单调增加。为了避免传感器数据库中的这个问题,需要使用除了时间戳以外的其他东西作为主键的第一个部分例如,可以在每个时间戳前添加传感器名称,这样会首先按传感器名称,然后按时间进行分区假设有多个传感器同时运行,写入负载将最终均匀分布在不同分区上。现在,当想要在一个时间范围内获取多个传感器的值时,您需要为每个传感器名称执行一个单独的范围查询。
Hash-based sharding
由于偏斜和热点的风险,许多分布式数据存储使用哈希函数来确定给定键的分区,即基于哈希的分片使用哈希函数处理密钥,然后使用结果获取分片

出于分区的目的,哈希函数不需要多么强壮的加密算法:例如,Object.hashCode()
和Object#hash
,同一个键可能在不同的进程中有不同的哈希值。一旦你有一个合适的键哈希函数,你可以为每个分区分配一个哈希范围(而不是键的范围
基于哈希的分片的一些典型示例是

与基于范围的分片相反,基于哈希的分片具有以下优点:密钥几乎是随机分布的,因此分布是均匀的。因此,它对于写入工作量和读取工作量几乎都是随机的系统更为友好。这是因为写入压力可以均匀地分布在群集中,从而使“范围扫描”之类的操作非常困难。
不幸的是,通过使用
请注意,基于哈希和基于范围的分片策略不是隔离的。相反,您可以灵活地组合它们。例如,您可以建立一个多级分片策略,该策略在最上层使用哈希,而在每个基于哈希的分片单元中,数据将按顺序存储。譬如在社交媒体网站上,一个用户可能会发布很多更新。如果更新的主键被选择为 (user_id, update_timestamp)
,那么您可以有效地检索特定用户在某个时间间隔内按时间戳排序的所有更新。不同的用户可以存储在不同的分区上,对于每个用户,更新按时间戳顺序存储在单个分区上。
策略选择
对于弹性可伸缩性,使用基于范围的分片的系统很容易实现:只需拆分[1,50)
和 [50,100)
。之后,将两个区域移动到两台不同的计算机中,并且负载达到平衡。
基于范围的分片可能会带来读写热点,但是可以通过拆分和移动消除这些热点。热点的拆分和移动落后于基于哈希的分片。但是总的来说,对于关系数据库,基于范围的分片是一个不错的选择。
相反,为使用基于哈希的分片的系统实现弹性可伸缩性非常昂贵。原因很明显。假定当前系统有三个节点,然后添加一个新的物理节点。在哈希模型中,
负载倾斜与消除热点
如前所述,哈希分区可以帮助减少热点。但是,它不能完全避免它们:在极端情况下,所有的读写操作都是针对同一个键的,所有的请求都会被路由到同一个分区。这种场景也许并不常见,但并非闻所未闻:例如,在社交媒体网站上,一个拥有数百万追随者的名人用户在做某事时可能会引发一场风暴。这个事件可能导致大量写入同一个键(键可能是名人的用户
如今,大多数数据系统无法自动补偿这种高度偏斜的负载,因此应用程序有责任减少偏斜。例如,如果一个主键被认为是非常火爆的,一个简单的方法是在主键的开始或结尾添加一个随机数。只要一个两位数的十进制随机数就可以将主键分散为
然而,将主键进行分割之后,任何读取都必须要做额外的工作,因为他们必须从所有