MongoDB 块与复合分片键平衡

MongoDB chunks balancing with compound sharding key

假设我有三个分片,使用复合键 { x: 1, y: 1 } 作为一个集合,x 有三个 int 值:1、2、3,y 是随机的。

然后我为 x = 1x = 2x = 3 插入相同数量的文档。我期望的是范围为 x = 1 的所有块转到 shard1,x = 2 转到 shard2,x = 3 转到 shard3,然后我可以进行查询隔离。但是输出是意外的:

test.t6

shard key: { "x" : 1, "y" : 1 }

chunks:
shard0000   5
shard0002   5
shard0001   5
{ "x" : { "$minKey" : 1 }, "y" : { "$minKey" : 1 } } -->> { "x" : 1, "y" : 0 } on : shard0000 Timestamp(2, 0) 
{ "x" : 1, "y" : 0 } -->> { "x" : 1, "y" : 11593 } on : shard0002 Timestamp(3, 0) 
{ "x" : 1, "y" : 11593 } -->> { "x" : 1, "y" : 34257 } on : shard0000 Timestamp(4, 0) 
{ "x" : 1, "y" : 34257 } -->> { "x" : 1, "y" : 56304 } on : shard0002 Timestamp(5, 0) 
{ "x" : 1, "y" : 56304 } -->> { "x" : 1, "y" : 78317 } on : shard0000 Timestamp(6, 0) 
{ "x" : 1, "y" : 78317 } -->> { "x" : 2, "y" : 3976 } on : shard0002 Timestamp(7, 0) 
{ "x" : 2, "y" : 3976 } -->> { "x" : 2, "y" : 26497 } on : shard0000 Timestamp(8, 0) 
{ "x" : 2, "y" : 26497 } -->> { "x" : 2, "y" : 48788 } on : shard0002 Timestamp(9, 0) 
{ "x" : 2, "y" : 48788 } -->> { "x" : 2, "y" : 74377 } on : shard0000 Timestamp(10, 0) 
{ "x" : 2, "y" : 74377 } -->> { "x" : 2, "y" : 99329 } on : shard0002 Timestamp(11, 0) 
{ "x" : 2, "y" : 99329 } -->> { "x" : 3, "y" : 25001 } on : shard0001 Timestamp(11, 1) 
{ "x" : 3, "y" : 25001 } -->> { "x" : 3, "y" : 49652 } on : shard0001 Timestamp(9, 2) 
{ "x" : 3, "y" : 49652 } -->> { "x" : 3, "y" : 72053 } on : shard0001 Timestamp(9, 4) 
{ "x" : 3, "y" : 72053 } -->> { "x" : 3, "y" : 97436 } on : shard0001 Timestamp(10, 2) 
{ "x" : 3, "y" : 97436 } -->> { "x" : { "$maxKey" : 1 }, "y" : { "$maxKey" : 1 } } on : shard0001 Timestamp(10, 3) 

我的假设是 MongoDB 不是那么聪明,它只是平衡节点之间的块数,它没有考虑复合键分组,对吗?或者我错过了什么?

balance chunks时的策略是什么?我了解它如何选择 from 侧和 to 侧,但文档没有说明它如何选择要移动的块。

谢谢。

My assumption is that MongoDB isn't that smart, it just balance chunks number among nodes, it dose not take compound key grouping into consideration, am I right? Or am I missing something?

您是正确的,因为 MongoDB 服务器(在 3.4 中)并没有试图过度考虑默认情况下如何分发块。块表示分片键范围 (by default up to 64MB) 中文档的逻辑范围,总体目标是每个分片的数据分布大致相等(由块数表示)。

但是,要将复合键分组置于上下文中,您需要考虑块分布如何影响读写用例。

正在从分片集群读取数据

查询从 cursor batches 中的服务器获取文档,不能超过最大 BSON 文档大小(当前为 16MB):

For most queries, the first batch returns 101 documents or just enough documents
to exceed 1 megabyte. Subsequent batch size is 4 megabytes. To override the
default size of the batch, see batchSize() and limit().

假设您没有更改批处理或块大小的任何默认值,这意味着 {x, y} 上的基于范围的查询仍然能够在单个块上填充来自单个块范围的许多批次目标分片(或偶尔超过一个取决于 size/distribution 文档和块)。

正在向分片集群写入数据

分片的主要原因之一是增加写入吞吐量。根据您的 choice of shard key 以及数据到达的方式,将连续分片键块的数据分布到不同的分片以避免潜在的热点可能会有好处。由于您的示例中 x 只有三个值,因此在不同分片上具有给定值 x 的范围可以通过跨分片并行写入来提高吞吐量。

平衡块的策略是什么?

I understand how it choose the from side and to side, but the docs didn't say anything about how it choose which chunk to move.

已超出 Sharded Collection Balancing is detailed in the MongoDB manual, but the short version is that the balancer waits until certain thresholds 的策略(具有最少和最多块的分片之间的差异)并且平衡回合将继续,直到该集合的任何两个分片上的块数之间的差异小于两个或一个块迁移失败。

为什么平衡器不更智能?

很难以适合所有工作负载和部署的方式概括平衡器策略。根据您的数据分布、分片键和访问模式,适用于一个用例的相同方法可能不支持您的方法。

有关此问题的一些讨论,请参阅 SERVER-5047: be smarter about which chunk moves when balancing 和相关问题。

一些平衡建议包括:

  • 基于指数排序的余额
  • 基于工作集估计的平衡
  • 使用随机分片分配
  • load based balancing

这些建议中的大多数都要求平衡器监视整个集群的其他指标,这会增加额外的复杂性和协调性。例如,通过某些负载指标(CPU、RAM、网络使用率)进行平衡听起来很有希望,直到您认为需要随时间跟踪这些指标(包括跨平台抽象)并且平衡器需要更复杂的策略来定义"balance" 阈值并忽略基于访问模式或服务器重启的临时不平衡。

是否有默认平衡器策略的替代方案?

一般来说,您可能希望使用默认平衡器策略,但是如果您认为有更合适的方式来平衡您的数据,可以考虑以下几种方法:

  1. 如果您希望您的数据具有某种特定的分片亲和力,可以在分片集群中使用名为 Sharding Zones (MongoDB 3.4+) or Tag-Aware Sharding (MongoDB 3.2 and older) that allows you to associate ranges of chunks with specific named shards. Use cases for this are typically more specialised as tagging can lead to an intentional imbalance of data. Some common use cases include optimising physical resources (e.g. tiered storage for "hot" and "cold" data), location-based separation of data (geo affinity), and balancing unsharded collections 的高级分片选项。

  2. 虽然强烈建议使用默认平衡器,但也可以 disable the balancer and Manually Migrate Chunks 使用 mongo shell 或通过实现您自己的平衡器脚本.