MongoDB 复合分片键

MongoDB compound shard key

我对 Mongo 复合分片键有疑问。假设我的文档结构如下:

{
   "players": [
      {
        "id": "12345",
        "name": "John",
      },
      {
        "id": "23415",
        "name": "Doe",
      }
   ]
}

Players embedded documents are always present and always 2. 我认为 "players.0.id" 和 "players.1.id" 作为分片键应该是一个不错的选择,因为它们不是单调的并且分布均匀。

我无法从文档中理解的是:

  1. 所有具有相同 "players.0.id" 或相同 "players.1.id" 的文档应该保存到同一个块中,或者
  2. 具有相同 "players.0.id" 和相同 "players.1.id" 的所有文档都应该保存到同一个块中。

换句话说,如果我查询集合以获取 John(作为玩家 1 或玩家 2)玩过的所有游戏,查询将发送到一个块还是所有块?

您不能创建部分键是多键索引(即数组字段上的索引)的分片键。 Shard Key Index Type:

中提到了这一点

A shard key index cannot be an index that specifies a multikey index, a text index or a geospatial index on the shard key fields.

如果 players 字段下恰好有两个项目,为什么不创建两个子文档而不是使用数组?数组通常适用于文档中有多项不确定编号的用例。例如,此结构可能适用于您的用例:

{
    "players": {
        "player_1": {
            "id" : 12345,
            "name": "John"
        },
        "player_2": {
            "id": 54321,
            "name": "Doe"
        }
    }
}

然后您可以创建如下索引:

> db.test.createIndex({'players.player_1.id':1, 'players.player_2.id':1})

为了回答你的问题,如果你正在使用这个片键,那么:

  1. 不能保证相同的 player_1.idplayer_2.id 会在同一个块上。这将取决于您的数据分布。

  2. 如果您以 player_1 OR player_2 的身份查询 John,该查询将发送到所有分片。这是因为您有一个复合索引作为分片键,并且您正在搜索非前缀字段上的精确匹配。

详细说明问题 2:

您正在查询的是:

db.test.find({$or: [
    {'players.player_1.id':123},
    {'players.player_2.id':123}
]})

在复合索引中,索引首先按player_1.id排序,然后对于每个player_1.id,存在排序的player_2.id。例如,如果您有 10 个包含 player_1.idplayer_2.id 值组合的文档,您可以像这样可视化索引:

player_1.id | player_2.id
------------|-------------
0           | 10
0           | 123
1           | 100
1           | 123
2           | 123
2           | 150
123         | 10
123         | 100
123         | 123
123         | 150

请注意,值 player_2.id: 123 在 table 中出现多次,每个 player_1.id 出现一次。另请注意,对于每个 player_1.id 值,player_2.id 值在其中排序。

这就是 MongoDB 的复合索引的工作原理和排序方式。复合索引有更多的细微差别,在这里解释太长了,但细节在 Compound Indexes page

中解释

这种排序方法的效果是,索引中分布着许多相同的 player_2.id 值。由于整体索引仅根据 player_1.id 排序,因此如果不指定 player_1.id 则无法找到准确的 player_2.id。因此,上述查询将发送到所有分片。