在MongoDB中使用UUID而不是ObjectID

时间:2015-03-06 08:43:12

标签: mongodb

出于性能原因,我们正在将数据库从MySQL迁移到MongoDB,并考虑将什么用于MongoDB文档的ID。我们正在讨论使用ObjectIDs,它是MongoDB的默认设置,还是使用UUID(这是我们迄今为止在MySQL中使用过的)。到目前为止,我们必须支持以下任何选项的论点如下:

的ObjectID: ObjectID是MongoDB的默认值,我假设(虽然我不确定)这是有原因的,这意味着我希望MongoDB可以比UUID更有效地处理它们,或者有其他理由选择它们。我还发现this stackoverflow answer提到ObjectIDs的使用使得索引更有效率,但是对于这个"更高效的"是

的UUID: 我们支持使用UUID的基本论点(并且它是非常重要的)是它们几乎可以通过任何数据库以这种或那种方式得到支持。这意味着如果在某种程度上我们决定从任何原因切换到MongoDB,我们已经有一个API,它根据ID从DB中检索文档,因为ID可以继续,所以这个API的客户端没有任何变化。完全一样。如果我们使用ObjectID,我不确定如何将它们迁移到另一个数据库。

有没有人对这些选项中的一个是否比另一个更好有什么见解?为什么?您是否曾在MongoDB中使用过UUID而不是ObjectIDs,如果是,您遇到的优势/问题是什么?

6 个答案:

答案 0 :(得分:34)

MongoDB的_id字段可以包含您想要的任何值,只要您可以保证它对于集合是唯一的。当您的数据已经具有自然键时,没有理由不使用它来代替自动生成的ObjectID。

提供ObjectID作为安全时间生成自己的唯一键的合理默认解决方案(并阻止初学者尝试复制SQL AUTO INCREMENT,这在分布式数据库中是一个坏主意。)

如果不使用ObjectID,您还会错过另一个便利功能:ObjectID在生成时还包含一个unix时间戳,并且许多驱动程序提供了一个提取它并将其转换为日期的功能。这有时会使单独的create-date字段变得多余。

但如果您不关心,则可以将您的UUID用作_id字段。

答案 1 :(得分:9)

我认为这是个好主意,Mongo也是如此。他们将UUID列为the _id field的常用选项之一。

注意事项:

  • 性能-如其他答案所述,benchmarks显示UUID导致插入性能下降。在最坏的情况下(集合中的文档从1000万增加到2000万),它们的速度要慢大约2-3倍-每秒插入2,000(UUID)和7,500(ObjectID)文档之间的差异。这是一个很大的差异,但其重要性完全取决于您的用例。您会一次批量插入数百万个文档吗?对于大多数我构建的应用程序,常见的情况是插入单个文档。在那个测试中,差异很多更小(6,250 -vs- 7,500;〜20%)。 ID类型根本不是限制因素。
  • 可移植性-当然,其他数据库确实倾向于提供良好的UUID支持,因此可移植性将得到改善。或者,由于UUID较大(更多位),因此可以repack an ObjectID into the "shape" of a UUID。这种方法不如直接可移植性好,但确实为您提供了前进的道路。

应对其他一些答案:

  • UUID具有本机支持 –您可以像使用ObjectID()一样使用Mongo Shell中的UUID() function;将字符串转换为等效的BSON对象。
  • UUID并不是特别大-与96位的ObjectID相比,它们是128位。 (它们应使用二进制子类型0x04进行编码。)
  • UUID可以包含时间戳-特别是,UUIDv1编码的时间戳具有60位精度,而ObjectID中只有32位。精度提高了6个数量级以上,因此是毫微秒而不是秒。实际上,这可能是一种比Mongo / JS Date对象更准确地存储创建时间戳的好方法,但是...
    • UUID()中的内置功能仅生成v4(随机)UUID,因此,要利用此功能,您需要依靠应用或Mongo驱动程序来创建ID。
    • 与ObjectID不同的是,由于the way UUIDs are chunked,时间戳记不能给您自然顺序。这可能是好事,也可能是坏事,具体取决于您的用例。
    • 在您的ID中包含时间戳记通常不是一个好主意。您最终会泄漏任何暴露ID的文档的创建时间。更糟糕的是,v1 UUID还为生成它们的机器编码一个唯一的标识符,该标识符可以公开有关您的基础架构的其他信息(例如,服务器数量)。当然,ObjectID也会对时间戳进行编码,因此对它们而言也是如此。

答案 2 :(得分:6)

考虑每种情况下您将存储的数据量。

MongoDB ObjectID的大小为12个字节,打包用于存储,其部分按性能组织(即首先存储时间戳,这是一个逻辑排序标准)。

相反,标准UUID是36个字节,包含短划线,通常存储为字符串。此外,即使您删除非数字字符并打算以数字方式存储,您仍必须满足其“indexy”部分(基于时间戳的UUID v1部分)位于UUID的中间,并且不会t很适合排序。有studies完成,允许高性能的UUID存储,我甚至写了Node.js library来协助管理。

如果您打算使用UUID,请考虑重新组织它以获得最佳索引和排序;否则你可能会遇到一个表演墙。

答案 3 :(得分:1)

在我遇到同样问题的时候,我发现这些Benchmarks。 它们基本上表明使用Guid而不是ObjectId会导致索引性能下降。

我会建议您自定义基准来模仿您的特定现实生活场景并查看数字的外观,不能100%依赖通用基准测试。

答案 4 :(得分:1)

我们必须小心区分MongoDB插入事物的成本与首先产生事物的成本 plus 相对于有效负载大小的成本。以下是一个小的矩阵,显示了生成_id的方法,该方法与可选的额外字节有效负载的大小交叉。测试仅使用javascript,在MacBook Pro本地主机上使用100个批次中的insertMany进行了100,000次插入,没有进行任何事务以尝试消除网络,聊天和其他因素。还进行了两次批处理= 1的运行,只是为了突出显着差异。


Method                                                                                         
A  :  Simple int:          _id:0, _id:1, ...                                                   
B  :  ObjectId             _id:ObjectId("5e0e6a804888946fa61a1976"), ...                       
C  :  Simple string:       _id:"A0", _id:"A1", ...                                             

D  :  UUID length string   _id:"9575edcc-cb70-4d63-97ed-ee5d624de87b0", ...                    
      (but not actually                                                                        
      generated by UUID()                                                                      

E  :  Real generated UUID  _id: UUID("35992974-21ea-4f61-b715-2dfaed663b73"), ...              
      (stored UUID() object)                                                                   

F  :  Real generated UUID  _id: "6b16f733-ff24-4172-83f9-e4f96ace6775"                         
      (stored as string, e.g.                                                                  
      UUID().toString().substr(6,36)                                                           

Time in milliseconds to perform 100,000 inserts on fresh (empty) collection.

Extra                M E T H O D   (Batch = 100)                                                               
Payload   A     B     C     D     E     F       % drop A to F                                  
--------  ----  ----  ----  ----  ----  ----    ------------                                   
None      2379  2386  2418  2492  3472  4267    80%                                            
512       2934  2928  3048  3128  4151  4870    66%                                            
1024      3249  3309  3375  3390  4847  5237    61%                                            
2048      3953  3832  3987  4342  5448  5888    49% 
4096      6299  6343  6199  6449  7634  8640    37%                                            
8192      9716  9292  9397 10816 11212 11321    16% 

Extra              M E T H O D   (Batch = 1)                                          
Payload   A      B      C      D      E      F       % drop A to F              
--------  -----  -----  -----  -----  -----  -----                              
None      48006  48419  49136  48757  50649  51280   6.8%                       
1024      50986  50894  49383  49373  51200  51821   1.2%                       


这是一项快速的测试,但是似乎很明显,基本字符串和整数与_id的速度大致相同,但是实际上生成一个UUID会增加时间-特别是如果您使用字符串UUID()对象的版本,例如UUID().toString().substr(6,36)还值得注意的是,构建ObjectId的速度似乎很快。

答案 5 :(得分:0)

过去几周我一直在考虑这个问题。我只是发现 ObjectId 和 UUID 都是独一无二的。事实上,在集合级别,无论使用什么类型,都不能有重复的 _id。一些答案谈到了插入性能。重要的是它与插入性能无关,它需要索引性能。这需要根据您将用于索引 _ids 的内存大小来计算。我们知道 ObjectId 是 12 个字节,而 UUID 是 36 个字节。它表示,对于相同数量的索引,如果您使用 UUID 而不是 ObjectId,您将需要 2 倍的内存空间。

所以从这个角度来看,在 mongodb 中最好使用 ObjectId 而不是 UUID。