【问题标题】:MongoDB and composite primary keysMongoDB 和复合主键
【发布时间】:2014-04-19 00:38:35
【问题描述】:

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

我看到了几种管理方法:

  1. 使用由 2 个值组成的主键对象(建议 here

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

  3. 将主键设为 2 个 uuid 的哈希

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

这些方法对性能有何影响?

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

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

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

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

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

更多信息:有关该问题的一些背景信息可以在here

【问题讨论】:

  • 可能最重要的问题是您将如何访问这些数据?写作显然插入 - 更新?查询呢?删除过吗?
  • 主要是写作。然后更新(主要是大爆发)。相当数量的插入(同样,主要是大爆发)。偶尔删除。
  • 更新将使用哪些字段?一个或两个 uuid?
  • 更新、删除和查询将针对这两个字段
  • 一起还是单独?或者每个? IE。是否每次更新都将针对单个唯一文档(因此提供两个 uuid 值)?

标签: mongodb composite-primary-key primary-key-design


【解决方案1】:

你应该选择选项 1。

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

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

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

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

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

另一个注意事项是_id:1 上的索引可用于查询:

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

但它可用于查询

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

【讨论】:

  • 好的,谢谢,我会记住的。其实是我自己想出来的。 BasicDBObject compositeKey = new BasicDBObject("deviceId", deviceID).append("id", id); String newID = compositeKey.toJson(); 其中 deviceID 和 id 包含我需要附加的值。再次感谢。
  • 至于您提到的第二个注意事项,您是否认为如果我们采用第一个选项(从两个值创建复合 ID),我们是否也应该复制这两个值?因此,我们将获得使用复合 ID 的性能优势,并且还能够查询值。
  • 伙计们,您确定点符号不起作用吗?对我来说确实如此。使用 MongoDB v4.2.6 dropbox.com/s/4z1jgch0lhnqebq/…
  • 哦,查询会起作用,只是无法使用 _id 索引。
  • 需要注意的一点是,如果主键发生变化,选项 1 将来将很难更改。与使用不同的“_id”重写所有数据相比,删除和添加新索引要容易得多。
【解决方案2】:

我有一个选项 4 给你:

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

  1. _id 索引将是连续的(尽管这在 MongoDB 中不太重要),易于分片,您可以让 MongoDB 管理它。
  2. 2 个 uuid 索引可让您进行所需的任何类型的查询(第一个、第二个或两者都按任意顺序),并且它们占用的空间比 1 个复合索引少。
  3. 如果您在同一查询中使用两个索引(以及其他索引)MongoDBintersect them(v2.6 中的新功能)就像您使用复合索引一样。

【讨论】:

  • 对顺序索引进行分片是个坏主意,它不会在所有分片之间均匀共享 + 您将填满无用的索引空间,因为您有 2 个索引而不是 1 个(对大型集合来说很重要) .
  • @JonathanMuller 如果您使用哈希对它们进行分片,则不会。 “散列键适用于像 ObjectId 这样单调增加的字段”docs.mongodb.org/manual/core/sharding-shard-key/…
【解决方案3】:

我会选择第二个选项,这就是为什么

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

【讨论】:

  • 谢谢,这很有帮助!只是为了澄清一下,您说拥有非顺序键可以帮助使用分片环境。但是,选项 #2 是 为您提供顺序 ID 的选项。我是不是有些不明白?
  • 您说得对,非顺序键注释与您的问题中的陈述有关,您所说的顺序键在 RDBMS 中很有帮助
  • 在大多数 RDBMS 中,记录物理存储在按主键排序的磁盘上。进行非顺序插入时,必须物理移动所有记录。这就是使用非顺序 ID 使插入变慢的原因。这也是为什么对聚集键的查询如此之快的原因。 (你可能已经知道了,但我只是想把我的问题放在上下文中)。你是说这不是 MongoDB 的情况吗?我的阅读使我相信这仍然是一个考虑因素。我知道标准的 MongoDB ObjectID 是连续的。我认为这是部分原因。
  • RDBMS 中的聚集索引意味着数据与索引保持相同的顺序。这需要额外的工作来在“之间”插入数据。在mongo中,没有聚集索引这样的东西,数据不需要按照_id索引的顺序添加到磁盘
  • 啊,这就是我所缺少的。非常感谢!
【解决方案4】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-08
    • 2011-02-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-16
    相关资源
    最近更新 更多