我们有一个大型的MongoDB集合,我们想要开始分片。该集合有3.4B记录,大小为14.6TB (5.3TB在磁盘上压缩)。这个收藏通常能看到每小时约500万次的写作,但我们预计这一数字将继续逐年增长。此集合上的索引大小为220 in。
所有记录都有一个feedId,所有查询都是针对属于特定feedId的记录的。目前有200个唯一的feedId值,但每个值的分布高度非线性.在低端,一些饲料可能每天只看到几十个记录。另一方面,前5个提要占数据集的75%。
记录也有一个timestamp,查询总是针对给定的日期范围。timestamp字段或多或少是单调的.
在feedId和timestamp上已经存在一个复合索引。
该集合的典型工作集仅是最后几周的数据,而且只占实际数据的一小部分。对此数据的查询必须非常快,对历史数据的查询速度较慢是可以接受的。因此,我们计划使用“标记”和/或“区域”将旧数据移动到具有较大HDD的节点,并使用带有SSD的节点作为“热”数据。
基于这些因素,使用{feedId: 1, timestamp: 1}的切分密钥是否合理?我的感觉是,由于feedId的非线性和timestamp的单调性,它可能会导致“热”节点。在键中添加“散列”字段会使其更好/更糟吗?
发布于 2018-04-09 14:58:09
所以让我们一点一滴地接受这个!
该集合有3.4B记录,大小为14.6TB (5.3TB在磁盘上压缩)
切分的性质是如此的重要,这是重要的是,这是正确的第一次通过。我将在这里详细介绍,但是TL;DR是:
mongodump --query)提取到暂存集群(例如使用mongorestore)现在,让我们深入研究:
目前有200个唯一的feedId值,但每个值的分布高度非线性.在低端,一些饲料可能每天只看到几十个记录。另一方面,前5个提要占数据集的75%。
因此,一个支持大量查询的字段的频率相当低。如果您只是在这个字段1上切分,您肯定会看到热点。
记录也有时间戳,查询总是针对给定的日期范围。时间戳字段或多或少是单调的。
因此,另一个字段支持大多数查询,但也不太适合分片2。
记录也有时间戳,查询总是针对给定的日期范围。时间戳字段或多或少是单调的。
对我来说,这意味着你要查询的主要字段是基于时间的。在给定的一段时间内,给我指定feedID的文档。您还将获得目标查询,因为查询碎片键的频率更高(例如,在一定时间范围内,或者在一定时间范围+feedId上)。3.
这也支持了分区的想法:
因此,我们计划使用“标记”和/或“区域”将旧数据移动到具有较大HDD的节点,并使用带有SSD的节点作为“热”数据。
通过分区,只要包含指向该键的整个前缀,您就可以使用shard键中的任何键。因此,{ feedId: 1, timestamp: 1 }主要支持feedId和时间戳上的区域,这并不是您想要的。4.
仅基于这一点,我敢说{ timestamp : 1, feedId : 1 }将是一个很好的选择。测试需要研究的是,在单调增加的字段中添加低频字段是否提供了良好的块分布。
现在,就哈希而言:
在键中添加“散列”字段会使其更好/更糟吗?
如果您的意思是,您的文档已经有一些散列字段,那么您肯定可以仅仅为了随机性而添加该字段。但如果你说的是散列的碎钥匙,那就不一样了。5
区域和散列的碎片键不能一起玩。散列碎片键的性质意味着块范围(因此是区域)表示哈希碎片键值。因此,即使您有两个值非常接近的文档,它们也可能以完全不同的块结束。因此,在一系列散列切分键值上创建一个区域,可能不会做您希望它做的事情。您可以使用带散列切分的区域将整个集合移动到集群中的碎片子集,但这不是您想要的。6
现在,您可能会遇到一个关键问题--您有一个庞大的集合。选择shard键可能会导致初始拆分的问题,在这种情况下,MongoDB试图将数据分割成块。请查看我们文档中的以下部分:对现有集合进行切分。有一个公式可供您使用,用于估计碎片键在配置的块大小(默认情况下为64 to )所支持的最大集合大小。我将猜测,最初您需要将块大小增加到128‘m或256’m。这仅适用于初始切分过程。之后,您可以将块大小减少到缺省值,并让MongoDB处理其余部分。
请注意,这将对性能产生影响。您将在碎片之间迁移块,再加上实际块分割的开销。我建议您在这里向我们的谷歌集团发布更具体的指导。
https://stackoverflow.com/questions/49671158
复制相似问题