MongoDB 分片键

MongoDB Sharding Key

我们有一个很大的 MongoDB 集合,我们想开始分片。该集合有 3.4B 条记录,大小约为 14.6TB(磁盘上压缩了 5.3TB)。该集合通常每小时写入约 5M,但我们预计这一数字将逐年增长。此集合的索引大小约为 220GB。

所有记录都有 feedId,所有查询都将针对属于特定 feedId 的记录。目前大约有 200 个独特的 feedId 值,但每个值的分布是高度非线性的。在低端,一些 feedId 每天可能只能看到几十条记录。另一方面,前 5 个 feedId 占数据集的 ~75%。

记录也有一个 timestamp,查询将始终针对给定的日期范围。 timestamp 字段或多或少是单调的。

feedIdtimestamp 上已经存在复合索引。

此集合的典型工作集只是最近几周的数据,因此只占实际数据的很小一部分。对该数据的查询必须非常快,对历史数据的较慢查询是可以接受的。因此,我们计划使用 "tags" and/or "zones" 将较旧的数据移动到具有更大 HDD 的节点,并使用具有 SSD 的节点来存储 "hot" 数据。

基于这些因素,使用 {feedId: 1, timestamp: 1} 的片键是否合理?我的感觉是,由于feedId的非线性和timestamp的单调性,可能会导致"hot"个节点。将 "hashed" 字段添加到密钥是否会使其成为 better/worse?

所以让我们一点一点地来吧!

The collection has 3.4B records and is ~14.6TB in size (5.3TB compressed on disk)

分片的性质使得在第一次通过时就把它做好很重要。我将在这里详细介绍,但 TL;DR 是:

  • 将数据集的一部分(例如使用 mongodump --query)提取到暂存集群(例如使用 mongorestore
  • 将示例工作负载指向暂存集群以模拟您的生产环境
  • 测试一个或多个分片键组合。 Dump/reload 根据需要,直到您对性能感到满意为止。

现在,让我们深入探讨:

There are currently ~200 unique feedId values, but the distribution across each value is highly non-linear. On the low end, some feedId's may only see dozens of records per day. On the other hand, the top 5 feedId's make up ~75% of the dataset.

因此,支持大量查询的一个字段的频率非常低。如果你只是在这个字段上分片,你肯定会看到热点 1

Records also have a timestamp and queries will always be for a given date range. The timestamp field is more-or-less monotonic.

所以另一个字段支持你的大部分查询,但也不适合分片 2

Records also have a timestamp and queries will always be for a given date range. The timestamp field is more-or-less monotonic.

这对我来说有点暗示您要查询的主要字段是基于 time 的。对于给定的 time 时间段,请提供具有指定 feedID 的文档。您还将获得有针对性的查询,因为您经常查询分片键(例如,在某个时间范围内,或者某个时间范围内 + feedId)。 3

这也支持你的分区想法:

As such, we're planning to use "tags" and/or "zones" to move older data to nodes with larger HDD's and use nodes with SSD's for the "hot" data.

通过分区,您可以使用分片键中的任何键,只要您包含通向该键的 整个 前缀即可。因此 { feedId: 1, timestamp: 1 } 将主要支持 feedId 时间戳上的区域,这并不是您想要的。 4

仅凭这一点,我敢说 { timestamp : 1, feedId : 1 } 会是一个不错的选择。您的测试需要研究的是是否添加 low-frequency 字段到单调递增字段提供良好的块分布。

现在,就散列而言:

Would adding a "hashed" field to the key make it better/worse?

如果您的意思是,您的文档 已经 有一些散列字段,那么您绝对可以添加它只是为了随机性。但是,如果您谈论的是 散列分片键 ,那就另当别论了。 5

区域和散列分片键不能一起玩。散列分片键的性质意味着块范围(因此区域)表示 散列 分片键值。因此,即使您有两个文档的值彼此非常接近,它们也很可能以完全不同的块结束。因此,在一系列散列分片键值上创建区域可能不会执行您想要的操作。您 可以 做一些事情,比如使用带有散列分片的区域将整个 collection 移动到集群中的分片子集,但这不是您想要做的。 6

现在有一个您可能 运行 进入的关键问题 - 您有一个 巨大 collection。您选择的分片键可能会导致初始拆分出现问题,其中 MongoDB 尝试将您的数据分成块。请查看我们文档中的以下部分:Sharding an Existing Collection。那里有一个公式供您使用来估计您的分片键可以支持的最大 collection 大小以及配置的块大小(默认情况下为 64MB)。我猜测您最初需要将块大小增加到 128MB 或 256MB。这仅在 initial 分片过程中需要。之后,您可以将块大小恢复为默认值,让 MongoDB 处理其余部分。

请注意,这会对性能产生影响。您将有块在分片之间迁移,加上实际块拆分的开销。我建议您 post 到我们的 Google Group 以获得更具体的指导。