我正在尝试确定在 mongo db 中处理复合主键的最佳方法。本系统中与数据交互的主键由 2 个 uuid 组成。 uuid 的组合保证是唯一的,但单个 uuid 都不是。
我看到了几种管理方法:
使用由 2 个值组成的主键对象(如此处建议) 使用标准自动生成的 mongo 对象 id 作为主键,将我的键存储在两个单独的字段中,然后在这两个字段上创建复合索引字段 使主键成为 2 个 uuid 的哈希值 我目前不知道的其他一些很棒的解决方案
这些方法的性能影响是什么?
对于选项 1,由于具有非顺序键,我担心插入性能。我知道这会扼杀传统的 RDBMS 系统,而且我已经看到迹象表明这在 MongoDB 中也是如此。
对于选项 2,拥有一个系统永远不会使用的主键似乎有点奇怪。此外,查询性能似乎不如选项 1。在传统的 RDBMS 中,聚集索引提供了最好的查询结果。这在 MongoDB 中的相关性如何?
对于选项 3,这将创建一个 id 字段,但在插入时它也不会是连续的。这种方法还有其他优点/缺点吗?
对于选项 4,嗯……选项 4 是什么?
此外,还有一些关于将来可能使用 CouchDB 代替 MongoDB 的讨论。使用 CouchDB 会提出不同的解决方案吗?
更多信息:可以找到有关该问题的一些背景here
你应该选择选项 1。
主要原因是您说您担心性能 - 使用始终存在且已经唯一的 _id 索引将使您不必维护第二个唯一索引。
对于选项 1,我担心插入性能对非顺序键的影响。我知道这会扼杀传统的 RDBMS 系统,而且我已经看到迹象表明这在 MongoDB 中也是如此。
您的其他选项并不能避免这个问题,它们只是将其从 _id 索引转移到辅助唯一索引 - 但现在您有两个索引,一个是右平衡的,另一个是随机访问的。
质疑选项 1 的原因只有一个,那就是您计划仅通过一个或仅通过另一个 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})
我有一个选项 4 给你:
使用自动 _id
字段并为两个 uuid 添加 2 个单字段索引,而不是单个复合索引。
_id 索引将是顺序的(尽管这在 MongoDB 中不太重要),易于分片,您可以让 MongoDB 管理它。 2 个 uuid 索引可让您进行所需的任何类型的查询(第一个、第二个或两者以任何顺序),它们占用的空间比 1 个复合索引少。如果您在同一个查询中同时使用两个索引(以及其他索引),MongoDB 将与它们相交(v2.6 中的新功能),就像您使用复合索引一样。
我会选择 2 选项,这就是为什么
拥有两个单独的字段,而不是像第 1 条中建议的那样从两个 uuid 连接的一个字段,将使您能够灵活地创建其他索引组合以支持未来的查询请求,或者如果结果证明一个键的基数高于另一个键。具有非顺序键可以帮助您在分片环境中插入时避免热点,所以它不是一个糟糕的选择。在我看来,分片是扩展集合上的插入和更新的最佳方式,因为写锁定是在数据库级别(2.6 之前)或集合级别(2.6 版本)
我会选择选项 2。您仍然可以创建一个同时处理 UUID 字段的索引,并且性能应该与复合主键相同,但使用起来会容易得多。
此外,根据我的经验,我从不后悔给某个东西一个唯一的 ID,即使它不是严格要求的。也许这是一个不受欢迎的意见。
BasicDBObject compositeKey = new BasicDBObject("deviceId", deviceID).append("id", id); String newID = compositeKey.toJson();
其中 deviceID 和 id 包含我需要附加的值。再次感谢。