【问题标题】:mongodb - shard key - compound vs hashmongodb - 分片键 - 复合与哈希
【发布时间】:2018-04-19 10:15:42
【问题描述】:

我正在使用现有的 mongodb 集合。数据如下所示:

{ user_id: 123, post: { id: 123456789, title: "..." } },
{ user_id: 123, post: { id: 123456790, title: "..." } },
{ user_id: 124, post: { id: 123456791, title: "..." } }

我需要对此集合进行分片,但我在选择分片键时遇到了问题。我经常根据用户执行操作(例如从用户 123 获取所有帖子)。我应该根据

创建一个分片键吗
{
  user_id: 1,
  post.id: 1
}

或相同,但散列?

如果它是散列的,我假设范围查询将被广播到所有分片。但是如果它散列,文档会均匀分布在分片上吗?您可以看到值单调增加。

谢谢,

编辑:我想我犯了一个错误,似乎复合索引不能被散列。来自文档(https://docs.mongodb.com/manual/core/index-compound):

您不能创建具有散列索引类型的复合索引。如果您尝试创建包含散列的复合索引,您将收到错误

我想这意味着这个问题是不明智的,所以我会关闭。

编辑 2:再想一想,这个问题是有效的,但这样表述会更好——我似乎有两个选择:

  1. 散列 post.id 字段,它应该是唯一的,如果散列将有助于确保数据在分片之间均匀分布,或者

  2. 创建 user_id 和 post.id 的复合键,如上面的代码。这也将保证唯一性,并且应该有助于单个用户的数据局部性。 但它会确保跨分片的数据分布均匀吗?

谢谢

【问题讨论】:

  • 您是否对 user_uid 进行基于范围的查询?
  • 不,仅针对每个用户(即从用户 123 获取所有帖子),或“其中 user_id IN (1,2,3)”
  • 最好的尝试方法是使用您的数据进行测试,使用所有最佳选择的分片键,然后尝试这些查询以检查分散-聚集或单个分片命中:)
  • 好的,谢谢。一般来说,在上述场景中,复合分片键有意义吗?例如,如果我使用“user_id”作为分片键,我会保证给定用户的所有帖子都在同一个分片上。但是那我怎么保证唯一性呢?我还是分片新手,我不想选择一个坏的分片键。谢谢

标签: mongodb sharding


【解决方案1】:

散列 post.id 字段,它应该是唯一的,如果散列将有助于确保跨分片的数据均匀分布

如果您的 ID 是单调的(根据当前示例),我强烈考虑使用 UUIDs/GUIDs,它可以在不依赖中心序列的情况下生成。除非您的序列号由另一个记录系统提供,否则它们将为需要声明下一个可用编号的分布式客户端带来扩展和协调挑战。 GUID 将更有效地实现您希望通过散列实现的结果。

MongoDB 的默认 ObjectId 是为此目的而设计的一个示例:一个伪随机 12 字节值,可以在分布式环境中独立生成,并根据前导时间戳进行一些近似排序。

生成自定义 UUID 超出了 MongoDB 的范围,但如果您有其他要求(长度、值的范围、排序、冲突的可能性……),有许多可用的算法/库可用于生成 UUID,或者您可以创建你自己的公式。

分片键值的基数决定了你是否会得到有效的数据分布。假设原始值存在基数,散列分片键有助于分配初始写入:这基本上将序列从单调递增变为均匀。

创建 user_id 和 post.id 的复合键,如上面的代码。这也将保证唯一性,并且应该有助于单个用户的数据局部性。 但它会确保跨分片的数据分布均匀吗?

分片键需要高基数,但不一定必须是唯一的。例如,如果您在 {month:1}(代表一年中的月份)的单个字段上进行分片,则该字段只有 12 个可能的值。一个月的所有数据最终都将保存在一个分片上,因此如果第 5 个月的值多于第 11 个月的值,则数据分布本质上是不均匀的。 MongoDB 用于分片集合的data distribution 是基于能够自动将分片键分成逐渐更小的键范围(称为)。一个基本假设是,每个块代表一个大致相等的数据范围(平均而言),并且块在分片之间的均匀分布将导致平衡。

对于您的用例,{user_id, post.id} 似乎是一个可能的复合分片键,假设您解决了 ID 单调递增的问题。这似乎符合我上面提到的三个方面。

但是,与其猜测分片键的结果,我建议在开发环境中进行测试。

如果您对自己的数据模型和分布模式有很好的理解或估计,我建议您在测试环境中使用代表性数据进行分片。如果需要,有许多有用的工具可用于生成虚假(但概率)数据。有关使用模式分析和“更像这样”方法的示例配方,请参阅:duplicate a collection into itself

【讨论】:

  • 再次感谢,斯坦尼。一个合理的解决方案是创建一个像user_id_hash = CryptoJS.MD5(user_id); 这样的额外字段,然后在{user_id_hash, post.id} 上创建一个唯一的复合分片键吗?这将很容易应用于现有文档,将缓解扩展问题,并有助于数据本地化。谢谢
  • @mils 是的,如果您已经有现有的文件,这是合理的解决方案。您需要在创建分片键索引之前添加哈希值,并且一旦集合被分片,分片键索引中的值将是不可变的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-04
  • 2012-07-26
  • 2019-05-03
  • 1970-01-01
  • 2023-03-22
  • 2017-07-13
相关资源
最近更新 更多