您的位置 首页 java

数据系统 – 分区Partitioning之键值数据

话说如果你有很庞大的数据没并且你想把它分区。那么你怎么决定那些数据记录存放在哪些节点中?

我们分区的目的是分散数据并且在节点间均衡查询负载。如果每个节点被公平的分散了负载,那么,理论上来说,10个节点应该能够处理单个节点10倍的数据和10倍的读写吞吐量(暂时忽略复制)。

如果负载是非公平分散的,以至于某些节点有着更多的数据或比其他节点接收更多的查询,我们称之为skewed(歪斜的)。skewed的出现是个分区更加的低效。在一个极端的用例中,所有的负载都会集中于某一个节点,所以10个里面的其他9个节点都是闲置的并且你的瓶颈就是那个单个繁忙节点。不成比例的高负载的分区称为hot spot热点。

避免hot spots的最简单的方法是随机把存储记录分配给节点。这将均衡的在节点间分发数据,但这也有一个缺点:当你试图读取一个特定的元素时,你没有办法知道它在哪个节点上,所以你必须并发的查询所有的节点。

我们可以做的更好。让我们假设现在你有一个简单的键值数据模型,你通过它的主键来访问数据记录。比方说,在一个老式的纸质百科全书中,你可以通过它的标题来查询条目;因为所有的条目的标题都是按照的字母顺序排序的,你可以快速的查找到你想要的。

通过键的范围分区

分区的一种方式就是分配一段连续范围(从最小到最大)的键给每个分区,就像一卷卷的纸质百科全书。如下图:

如果你知道范围的边界,你就可以判定那个分区包含了哪些键。如果你知道哪个分区分配给了哪个节点,那么你就可以直接向合适的节点发起请求了(或者,拿百科全书作例,就好比直接从书架行挑选想要的书籍)。

键的范围不是必须均衡分配的,因为你的数据可能不是均衡分布的。比方说,就那上图为例,卷一包含了以A和B为首字母的词汇,而卷12包含的词汇是以T、U、V、X、Y以及Z为首字母的。简单的让每一卷包含两个字母的标题词汇会导致一些卷的体积比其他卷大的多。为了均衡的分布数据,分区的边界需要去匹配数据。

分区的边界可能是管理员手动挑选的,或者数据库自动挑选的。这种分区策略被Bigtabel所使用,它的开源等效于HBase、RethinkDB以及2.4版本前的 MongoDB

在每个分区中,我们保持键是有序排列的。这对范围扫描是便利的,并且您可以将键视为级联 索引 ,以便在一个查询中获取多个相关记录。比方说,设想一个应用存放来自传感器网络的数据,键是测量的时间戳(year-month-day-hour-minute-second)。在这样的用例中,范围扫描是很有用的,因为它可以让你简单的获取到某个特定月的所有读取到的数据。

然而,键的范围分布的缺点是某些获取模式会导致hot spots。如果键是时间戳,那么分区就反映了时间的范围,比如,一个分区代表一天,不幸的时,随着测量的发生,我们把来自传感器的数据写入数据库,所有的写都会集中于同一个分区(代表今天的节点),所以那个分区会因为写而过载而其他的则闲置着。

为了避免这个在传感器数据库中的问题,你需要使用某些其他的手段而不是时间错来作为键的首要元素。比方你可以用传感器的名字作为每个时间戳的前缀,以便分区首先是按照传感器名字而不是以时间。假定你在同一时间有很多活动着的传感器,那么写的父爱会更加均衡的分散到分区之间。现在,当你想要获取一段时间范围内的多个传感器的数值时,你需要针对每个传感器名字来做范围查找。

通过键的哈希来分区

因为skew和hot spots的风险,许多分布式数据存储对于给定的键使用哈希函数来决定分区。

一个好的哈希函数会获取skewed的数据,然后使其均匀分布。假设你有一个以 字符串 为参数的32位的哈希函数。当你给予一个新的字符串时,它会返回一个区间在0~2^32中的看似随机的数。即使数据的字符串相似,它们的哈希值会按照数值的范围均衡的分布。

对于分区来说,哈希值不需要被强有力的加密:比方说,Cassandra以及MongoDB使用 MD5 ,Voldemort使用Fowler-Noll-Vo函数。许多编程语言都有内置的简单的哈希函数,但它们并不适合分区:比方说,在java中的 Object .hashCode()和Ruby中的Object#hash,在不同的进程中,相同的值会有不同的哈希值。

一旦你有了一套对于键来说合适的哈希函数,那么你就可以按照哈希值的范围来分配每个分区,而不是按照键的范围,并且,每个落入其哈希值范围的分区的键将被存储在那个分区。如下图所示:

这个技术好在可以均衡在分区间分布键。分区的边界可以平均分配,或是可以伪随机地选择它们(在这种情况下,该技术有时称为一致性哈希算法)。

然而,不幸的是,对分区使用键的哈希会让我们丢失掉按照键范围分区的一个美好的特性:高效范围查找的能力。之间邻接的键现在会分散在所有的分区中,所以他们的排序也就丢失了。在MongoDB中,如果你已经使能了基于哈希的分区模式,任何的范围查找都会被发送至所有的分区。Riak,Couchbase或是Voldemort都不支持主键的范围查找。

Cassandra在两种分区策略中达成了一种折中。可以使用由几列组成的复合主键声明Cassandra中的表。仅对该键的第一部分进行哈希处理才能确定分区,而其他列则用作连接索引,用于对Cassandra的SSTables中的数据进行排序。因此,查询无法在复合键的第一列中搜索值的范围,但是如果它为第一列指定了固定值,则可以对键的其他列执行有效的范围扫描。

级联索引方法为一对多关系提供了优雅的数据模型。例如,在社交媒体网站上,一个用户可以发布许多更新。如果将更新的主键选择为(user_id,update_timestamp),那么您可以有效地检索特定用户在某个时间间隔内按时间戳排序的所有更新。不同的用户可能存储在不同的分区上,但是在每个用户内,更新是按时间戳顺序存储在单个分区上的。

Skewed工作负载和对Hot Spots的缓解

按照我们之前讨论的,对一个键进程哈希来决定它的分区可以帮助降低hot spots。然而,它也不能完全避免:在极端情况下,所有的读取和写入都针对同一个键,你仍旧最终会把所有的请求最终路由到同一个分区中。

这种类型的工作负载也许是很少见的,但不是没有听说过:比方说,在一个社交媒体网站上,一个有着数百万粉丝的明星,会因为某些举动而造成很大范围的影响。这样的事件会导致大量对于同一个键的写请求(这个键可以是明星的用户ID,或是人们进行评论的活动ID)。对这个键进行哈希是没有帮助的,因为对两个相同ID的哈希仍旧是相同的。

如今,大多数的数据系统不能自动补偿这类搞skewed的工作负载,所以应用程序应该负责降低这类skew。比方说,如果已知一个键的热度很高,简单的方法就是在这个key的开头或结尾添加一个随机数。只需两位十进制随机数,就可以将对键的写入平均分配到100个不同的键中,从而可以将这些键分配到不同的分区中。

然而,把写分散到不同的键中,任何的读取都会需要额外的工作,也就是说必须从上面100个键中读取数据然后组合它们。此技术还需要额外的簿记:仅在少量热键后附加随机数,这才有意义。对于写入吞吐量低的绝大多数键,这将是不必要的开销。因此,您还需要某种方式来跟踪正在拆分的键。

也许在将来,数据系统能够自动侦测和补偿skewed工作负载,但对于现在,你需要在对你自己的应用考虑怎么权衡它。

文章来源:智云一二三科技

文章标题:数据系统 – 分区Partitioning之键值数据

文章地址:https://www.zhihuclub.com/190830.shtml

关于作者: 智云科技

热门文章

网站地图