我们有一个很大的MongoDB集合,我们想开始分片。该集合具有3.4B记录,大小约为14.6TB(磁盘上压缩5.3TB)。这个集合通常会看到每小时约5M的写入,但我们预计这将继续逐年增长。这个系列的索引大小约为220GB。
所有记录都有feedId
,所有查询都将针对属于特定feedId
的记录。目前有大约200个唯一feedId
值,但每个值的分布是高度非线性的。在低端,一些feedId每天只能看到几十条记录。另一方面,前5个feedId占数据集的约75%。
记录也有timestamp
,查询将始终针对给定的日期范围。 timestamp
字段或多或少是单调的。
feedId
和timestamp
已存在复合索引。
此集合的典型工作集仅是过去几周的数据,因此仅占实际数据的很小一部分。对此数据的查询必须非常快,对历史数据的查询速度较慢是可以接受的。因此,我们计划使用"标签"和/或"区域"将较旧的数据移动到具有较大HDD的节点,并使用带有SSD的节点用于“热”#34;数据
基于这些因素,使用{feedId: 1, timestamp: 1}
的分片键是否合理?我的感觉是它可能会导致“热”"节点由于feedId
的非线性和timestamp
的单调性质。会添加一个"哈希"关键的字段使它更好/更差?
答案 0 :(得分:1)
所以让我们一点一点地抓住这个!
该集合有3.4B记录,大小约为14.6TB(磁盘压缩5.3TB)
分片的本质是,第一次通过这个权利非常重要。我将在这里详细介绍,但TL; DR是:
mongodump --query
)提取到临时群集(例如,使用mongorestore
)现在,让我们深入研究:
目前有大约200个唯一的feedId值,但每个值的分布是高度非线性的。在低端,一些feedId每天只能看到几十条记录。另一方面,前5个feedId占数据集的约75%。
因此,支持大量查询的一个字段的频率非常低。如果你只是在这个字段1
上进行分片,你肯定会看到热点记录也有时间戳,查询将始终针对给定的日期范围。时间戳字段或多或少是单调的。
另一个支持大多数查询的字段,但也不适合分片2
记录也有时间戳,查询将始终针对给定的日期范围。时间戳字段或多或少是单调的。
这对我来说意味着您要查询的主要字段是 time 。对于给定的时间段,请为我提供具有指定feedID的文档。您还将获得有针对性的查询,因为您经常查询分片密钥(例如,在一段时间内,或者在一段时间内feedId
)。 3
这也支持您的分区想法:
因此,我们计划使用“标签”和/或“区域”将较旧的数据移动到具有较大HDD的节点,并将具有SSD的节点用于“热”数据。
使用分区,您可以使用分片键中的任何键,只要包含导致该键的整个前缀即可。因此{ feedId: 1, timestamp: 1 }
主要支持feedId 和时间戳上的区域,这不是您想要的。 4
仅基于此,我敢冒险{ timestamp : 1, feedId : 1 }
将是一个很好的选择。您的测试需要考虑的是是否添加
低频场到单调增加的场提供了良好的块分布。
现在,就哈希而言:
在密钥中添加“哈希”字段会使其更好/更差吗?
如果您的意思是,您的文档已经有一些哈希字段,那么您肯定可以添加它只是为了随机性。但是,如果你在谈论一个散列的碎片密钥,那么这是一个不同的故事。 5
区域和散列分片键不能一起播放。散列分片键的性质意味着块范围(以及区域)表示散列分片键值。因此,即使您有两个文档的值非常接近,它们也可能最终会出现完全不同的块。因此,在一系列散列的分片键值上创建区域可能不会按照您的要求执行。你可以做一些事情,比如使用带有散列分片的区域来将整个集合移动到集群中的分片子集上,但这不是你想要做的。 6
现在您可能遇到一个关键问题 - 您有一个巨大的集合。您选择的分片键可能会导致初始分割出现问题,MongoDB会尝试将您的数据划分为块。请查看我们文档中的以下部分:Sharding an Existing Collection。您可以使用一个公式来估计分片键可以使用配置的块大小(默认为64MB)支持的最大集合大小。我猜你需要将块大小增加到128MB或者最初可能是256MB。这仅适用于初始分片过程。之后,您可以将块大小减少回默认值,让MongoDB处理其余部分。
请注意,这会对性能产生影响。您将拥有跨分片迁移的块,以及实际块分割的开销。我建议您在Google Group发帖,以获取更具体的指导。