在MongoDB中使用UUID而不是ObjectID

vsdwdz23  于 2022-11-03  发布在  Go
关注(0)|答案(8)|浏览(284)

出于性能原因,我们正在将数据库从MySQL迁移到MongoDB,并考虑使用什么作为MongoDB文档的ID。我们正在讨论是使用ObjectID(MongoDB的默认值)还是使用UUID(这是我们在MySQL中一直使用的)。到目前为止,我们必须支持以下任何一种选择:

**ObjectID:**ObjectID是MongoDB的默认值,我认为(虽然我不确定)这是有原因的,这意味着我希望MongoDB可以比UUID更有效地处理它们,或者有其他原因更喜欢它们。我还发现this stackoverflow answer提到使用ObjectID可以使索引更有效,但是如果有一些指标来衡量这种“更有效”的程度,那就太好了。
**UUID:**我们支持使用UUID的基本论点(这是一个相当重要的论点)是,几乎任何数据库都以某种方式支持UUID。这意味着,如果我们决定从MongoDB切换到其他数据库,无论出于什么原因,我们已经有了一个API,它基于ID从DB中检索文档,由于ID可以继续保持完全相同,所以对于这个API的客户端来说没有任何变化。如果我们要使用ObjectID,我真的不确定我们将如何将它们迁移到另一个DB。

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

tcbh2hod

tcbh2hod1#

在Mongo中使用UUID当然是可能的,并且得到了很好的支持。例如,Mongo文档将UUID列为_id字段的常用选项之一。

注意事项

*性能-正如其他答案所提到的,benchmarks show UUID会导致插入性能下降。(集合中的文档数量从1000万增加到2000万),速度大约慢2- 3倍-插入2,000个(UUID)和7,500(ObjectID)文档。这是一个很大的差异,但其重要性完全取决于您的用例。您是否会一次批量插入数百万个文档?对于大多数应用程序,我'同样的基准测试显示,对于这种使用模式,差异要小得多(6,250对7,500;~20%)。不是微不足道的。。但也不是惊天动地的。
*可移植性-许多其他数据库平台都有很好的UUID支持,因此可移植性会得到改善。或者,由于UUID更大(更多位),因此可以repack an ObjectID into the "shape" of a UUID。这种方法不如直接可移植性好,但它确实为您提供了一种在现有ObjectID和UUID之间“Map”的方法。
*去中心化-UUID的一大卖点是它们是普遍唯一的。这使得以去中心化的方式在任何地方生成它们变得切实可行(例如,与自动递增的值相反,它需要集中的真实值源来确定“下一个”值)。当然,Mongo对象ID也具有此优点。不同之处在于,UUID基于已有15年以上历史的标准,并在(几乎?)所有平台、语言等。如果您需要创建实体,这使得它们非常有用(或者具体地说,是一组 * 相关 * 的实体),而不需要与数据库交互。然后在将来的某个时候将整个图形写入数据库而不发生冲突。虽然这也可以用Mongo ObjectID实现,但是找到代码来生成它们/处理这种格式通常会比较困难。

纠正次数
与其他一些答案相反:

*UUID确实有本机Mongo支持-您可以在Mongo Shell中以与使用ObjectID()完全相同的方式使用UUID() function;将UUID字符串转换为等效的BSON对象。
*UUID不是特别大-当使用二进制子类型0x04编码时,它们是128位,而ObjectID是96位。(如果编码为字符串,它们 * 将 * 相当浪费,大约需要288位。)
*UUID可以包含时间戳-具体来说,UUIDv 1编码一个60位精度的时间戳,而ObjectID是32位。在十进制中,这是超过6个数量级的精度-所以 * 纳秒 * 而不是秒。它实际上可以是一个不错的存储创建时间戳的方式,比Mongo/JS Date对象支持的精度更高,但是...

  • 内置的UUID()函数只生成v4(随机)UUID,因此,要利用此功能,您需要依靠您的应用程序或Mongo驱动程序来创建ID。
  • 与ObjectID不同的是,由于UUID的分块方式,时间戳并没有给予一个自然的顺序。这可能是好的,也可能是坏的,这取决于您的用例。(新标准可能会改变这一点;请参阅下面2021年更新。)
  • 在ID中包含时间戳有时是个坏主意。在任何公开ID的地方,最终都会泄漏文档的创建时间。(当然,ObjectID也会对时间戳进行编码,因此这在一定程度上也适用于它们。)
  • 如果你用(符合规范的)v1 UUID来做这个,你也在编码服务器MAC地址的一部分,它可能 * 潜在地 * 被用来识别机器。请参阅下面2021年更新。)

结论
如果您孤立地考虑Mongo DB,ObjectID是显而易见的选择。它们开箱即用,并且是一个完全可用的默认值。使用UUID * 确实 * 增加了一些麻烦,无论是在处理值时(需要转换为二进制类型,等等)以及在性能方面。这种小小的不便是否值得使用标准化的ID格式,实际上取决于您对可移植性的重视程度和您的体系结构选择。
您会在不同的数据库平台之间同步数据吗?您将来会将数据迁移到不同的平台吗?您是否需要在数据库 * 外部 *、其他系统或浏览器中生成ID?如果现在不需要,将来某个时候也可以?UUID可能值得您费心。

2021年8月更新

IEFT最近发布了UUID规范的更新草案,将引入一些新版本的格式。

具体来说,UUIDv6和UUIDv7基于UUIDv1,但翻转了时间戳块,使位从最重要的位排列到最不重要的位。这使结果值具有自然顺序,该顺序(或多或少)反映了它们的创建顺序。新版本还排除了从服务器MAC地址派生的数据,解决了对v1 UUID的长期批评。
这些变化需要时间来实现,但(恕我直言)它们显著地现代化和改进了格式。

nbnkbykc

nbnkbykc2#

MongoDB的_id字段可以有任何值,只要你能保证它在集合中是唯一的。当你的数据已经有了自然键,没有理由不使用它来代替自动生成的ObjectID。
ObjectID是作为一种合理的默认解决方案提供的,它可以安全地生成自己的唯一键(并阻止初学者尝试复制SQL的AUTO INCREMENT,这在分布式数据库中是一个坏主意)。
如果不使用ObjectID,您还将错过另一个方便的功能:ObjectID在生成时还包含一个unix时间戳,许多驱动程序提供了一个函数来提取它并将其转换为日期。这有时会使单独的create-date字段变得多余。
但是,如果这两个问题都不需要考虑,您可以自由地将UUID用作_id字段。

jckbn6z7

jckbn6z73#

请考虑在每种情况下要存储的数据量。
MongoDB ObjectID的大小为12字节,为存储而打包,其各部分为性能而组织(即首先存储时间戳,这是逻辑排序标准)。
相反,标准的UUID是36字节,包含破折号,通常存储为字符串。(UUID v1中基于时间戳的部分)位于UUID的中间,并且不适合进行排序。已经完成了studies,它允许进行高性能的UUID存储,我甚至编写了一个Node.js library来帮助管理它。
如果您打算使用UUID,请考虑重新组织它以获得最佳索引和排序;否则很可能会遇到性能瓶颈。

hs1rzwqc

hs1rzwqc4#

我们必须小心区分MongoDB插入一个东西的成本和生成这个东西的成本 * 加上 * 与有效负载大小相关的成本。下面是一个小矩阵,显示了生成_id的方法,与可选的额外有效负载字节的大小交叉。测试仅使用javascript,在MacBook Pro localhost上进行,100。000次插入,尝试消除网络、聊天和其他因素。还使用batch = 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%

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

2vuwiymt

2vuwiymt5#

我发现这些Benchmarks前一段时间,当我有同样的问题。他们基本上表明,使用Guid而不是ObjectId导致索引性能下降。
无论如何,我会建议您自定义的基准,以模仿您的具体真实的生活中的情况,看看如何数字看起来像,一个人不能依赖100%的通用基准。

brc7rcf0

brc7rcf06#

过去几周我一直在考虑这个问题。我只是发现ObjectId和UUID都是唯一的。事实上,在集合级别,无论使用什么类型,都不能有duplicate _id。一些答案谈到了插入性能。重要的是,这不是插入性能,而是索引性能。我们知道ObjectId是12字节,而UUID是36字节,这意味着对于相同数量的索引,如果使用UUID而不是ObjectId,则需要2倍多的内存空间。
所以从这个Angular 来看,在mongodb中使用ObjectId比UUID更好。

iklwldmw

iklwldmw7#

UUID128位(16字节),并且是全局唯一的。请参阅RFC 4122
Object Ids是MongoDB特定的结构,为96位(12字节)。虽然它足以提供全局唯一性,但存在一些边缘条件。MongoDB使用this official document来比较两者。
我们不希望被MongoDB特定的ID生成所束缚,而更希望在客户端进行。我们还使用多种数据库。底线是,选择UUID而不是ObjectId是一个可以根据他们的特定用例做出的决定。

bkhjykvo

bkhjykvo8#

试试这个

const uuid = require('uuid')
const mongoose = require('mongoose')
const YourSchema = new Schema({
  _id:{
    type: String,
        default: () => uuid.v4().replace(/\-/g, ""), 
   }

})

相关问题