73

我正在尝试确定在 mongo db 中处理复合主键的最佳方法。本系统中与数据交互的主键由 2 个 uuid 组成。uuid 的组合保证是唯一的,但单个 uuid 都不是。

我看到了几种管理方法:

  1. 对由 2 个值组成的主键使用一个对象(如此处所建议

  2. 使用标准自动生成的 mongo 对象 id 作为主键,将我的键存储在两个单独的字段中,然后在这两个字段上创建复合索引

  3. 使主键成为 2 个 uuid 的哈希

  4. 我目前不知道的其他一些很棒的解决方案

这些方法的性能影响是什么?

对于选项 1,由于具有非顺序键,我担心插入性能。我知道这会扼杀传统的 RDBMS 系统,而且我已经看到迹象表明这在 MongoDB 中也是如此。

对于选项 2,拥有一个系统永远不会使用的主键似乎有点奇怪。此外,查询性能似乎不如选项 1。在传统的 RDBMS 中,聚集索引提供了最好的查询结果。这在 MongoDB 中的相关性如何?

对于选项 3,这将创建一个 id 字段,但在插入时它也不会是连续的。这种方法还有其他优点/缺点吗?

对于选项 4,嗯……选项 4 是什么?

此外,还有一些关于将来可能使用 CouchDB 代替 MongoDB 的讨论。使用 CouchDB 会提出不同的解决方案吗?

更多信息:可以在此处找到有关该问题的一些背景

4

4 回答 4

54

你应该选择选项 1。

主要原因是您说您担心性能 - 使用始终存在且已经唯一的 _id 索引将使您不必维护第二个唯一索引。

对于选项 1,我担心插入性能对非顺序键的影响。我知道这会扼杀传统的 RDBMS 系统,而且我已经看到迹象表明这在 MongoDB 中也是如此。

您的其他选项并不能避免这个问题,它们只是将其从 _id 索引转移到辅助唯一索引 - 但现在您有两个索引,一个是右平衡的,另一个是随机访问的。

质疑选项 1 的原因只有一个,那就是您计划仅通过一个或仅通过另一个 UUID 值访问文档。只要您始终提供这两个值并且(这部分非常重要)您始终在所有查询中以相同的方式对它们进行排序,那么 _id 索引将有效地发挥其全部目的。

作为详细说明为什么您必须确保始终以相同的方式对两个 UUID 值进行排序,当比较子文档时{ a:1, b:2 }不等于{ b:2, a:1 }- 您可能有一个集合,其中两个文档具有这些 _id 值。因此,如果您首先使用字段 a 存储 _id,那么您必须始终在所有文档和查询中保持该顺序。

另一个注意事项是 index on_id:1将可用于查询:

db.collection.find({_id:{a:1,b:2}}) 

但它不能用于查询

db.collection.find({"_id.a":1, "_id.b":2})
于 2014-04-26T22:43:53.087 回答
10

我有一个选项 4 给你:

使用自动_id字段并为两个 uuid 添加 2 个单字段索引,而不是单个复合索引。

  1. _id索引将是顺序的(尽管在 中不太重要MongoDB),易于分片,并且您可以MongoDB对其进行管理。
  2. 2 个 uuid 索引可让您进行所需的任何类型的查询(第一个、第二个或两者以任何顺序),它们占用的空间比 1 个复合索引少。
  3. 如果您在同一个查询中同时使用两个索引(以及其他索引),它们MongoDB与它们相交(v2.6 中的新功能),就像您使用复合索引一样。
于 2014-04-28T11:42:02.710 回答
7

我会选择 2 选项,这就是为什么

  1. 拥有两个单独的字段,而不是像第 1 条中建议的那样从两个 uuid 连接的一个字段,将使您能够灵活地创建其他索引组合以支持未来的查询请求,或者如果结果证明一个键的基数高于另一个键。
  2. 具有非顺序键可以帮助您在分片环境中插入时避免热点,所以它不是一个糟糕的选择。在我看来,分片是扩展集合上的插入和更新的最佳方式,因为写锁定是在数据库级别(2.6 之前)或集合级别(2.6 版本)
于 2014-04-26T08:09:31.550 回答
4

我会选择选项 2。您仍然可以创建一个同时处理 UUID 字段的索引,并且性能应该与复合主键相同,但使用起来会容易得多。

此外,根据我的经验,我从不后悔给某个东西一个唯一的 ID,即使它不是严格要求的。也许这是一个不受欢迎的意见。

于 2014-04-22T18:15:26.567 回答