深入探讨MongoDB中的GUID/UUID使用

在现代软件开发中,唯一标识符(如GUID/UUID)的使用非常普遍。开发者经常需要在数据库中显式存储这些标识符,而不是依赖数据库系统生成的标识符。MongoDB及其驱动程序为GUID/UUID数据类型提供了内置支持,使得开发者可以立即开始使用GUID/UUID,而无需审查数据库规范。在大多数情况下,这种便利性是足够的。然而,在某些情况下,可能会遇到一些难以调试的意外问题。

当部署涉及多语言时,可能会发现应用程序不再按预期工作。例如,可能无法将订单与客户匹配。此外,数据可能会随着时间的推移而损坏,如果没有定期备份,将很难从这种灾难中恢复。

在本文中,将更深入地探讨使用GUID/UUID可能变得更加复杂的场景。将使意识到这些配置,并为提供一套最佳实践。

什么是UUID?什么是GUID?

通用唯一标识符(UUID)是计算机软件中用作标识符的唯一数字。有时,术语全局唯一标识符(GUID)用来描述它。GUID是UUID标准的多种实现之一。UUID是128位值,通常显示为32个十六进制数字,用连字符分隔,例如:

176BF052-4ECB-4E1D-B48F-F3523F7F277F

(上面的示例是一个随机的版本4 UUID)

MongoDB和GUID/UUID支持

MongoDB内置了对GUID/UUID数据类型的支持,大多数MongoDB驱动程序也原生支持GUID/UUID。MongoDB本身将它们存储为二进制字段,当从软件访问这些二进制字段时,MongoDB驱动程序通常会将数据库中找到的值转换为特定于语言的GUID或UUID对象。例如,当使用C#驱动程序从MongoDB数据库读取UUID时,将返回一个类型为System.GUID的对象。另一方面,Java驱动程序将返回一个类型为java.util.UUID的对象。

这使得从应用程序代码中使用GUID/UUID数据类型变得非常方便和容易。

然而,有一个陷阱

然而,当在多平台/多语言环境中使用UUID时,可能会遇到问题,因为使用不同的MongoDB驱动程序的不同编程语言访问数据库可能并不总是安全的,正如将在下面展示的。此外,当在第三方MongoDB管理软件中使用UUID数据类型时,应该小心,因为只有少数MongoDB工具在处理UUID数据类型时会给予适当的关注。

MongoChef是一个MongoDBGUI的例子,它提供了不同的选项来确保UUID被正确地解释、写入和更新。

MongoDB驱动程序通常在其二进制数据库表示和特定于语言的UUID数据类型之间转换UUID。最初,编码方法是平台特定的,并没有在不同的MongoDB驱动程序中一致实现。

二进制子类型0x03和0x04

在审查BSON规范时,会注意到两个二进制子类型被分配给UUID数据类型:最初,只有0x03子类型被用来指定UUID。为了解决由于特定于语言的UUID实现而产生的多平台环境中的可移植性问题,MongoDB设计者后来引入了新的0x04子类型。

使用0x04子类型存储UUID为二进制字段的字节顺序现在在所有MongoDB驱动程序中一致实现,而遗留的0x03子类型仅用于遗留数据。

使用二进制子类型0x04处理UUID时,可以保证能够从任何平台和任何编程语言访问数据,而不会出现这些兼容性问题。

最佳实践

1. 确保总是知道应用程序使用的UUID子类型是什么。可以通过查看现有数据的JSON表示(例如,在mongo shell中)来找出使用的子类型:

BinData(3,"B0AFBAMCAQAPDg0MCwoJgA==")

代表以二进制字段形式存储的UUID,使用遗留的0x03子类型

BinData(4,"SDcTgfx0SOq0Cl7DMRAzDQ==")

代表以二进制字段形式存储的UUID,使用0x04子类型

也可以使用支持遗留UUID的MongoDB GUI来访问这些信息。MongoChef将常规UUID注释为Binary - UUID,对于遗留UUID,它让知道当前使用的编码(例如Java、C#或无):

2. 配置驱动程序,以便在新部署中使用子类型0x04。MongoDB驱动程序通常将UUID存储为分配了遗留0x03子类型的二进制字段。这个配置可以更改:

C#:

BsonDefaults.GuidRepresentation = GuidRepresentation.Standard;

也可以在服务器、数据库和集合级别修改GuidRepresentation。

Python:

# 配置Python驱动程序的行为。阅读更多关于uuid_subtype属性的信息。

Java:

/* * 将UUID对象转换为带有子类型0x04的二进制 */ public static Binary toStandardBinaryUUID(java.util.UUID uuid) { long msb = uuid.getMostSignificantBits(); long lsb = uuid.getLeastSignificantBits(); byte[] uuidBytes = new byte[16]; for (int i = 15; i >= 8; i--) { uuidBytes[i] = (byte) (lsb & 0xFFL); lsb >>= 8; } for (int i = 7; i >= 0; i--) { uuidBytes[i] = (byte) (msb & 0xFFL); msb >>= 8; } return new Binary((byte) 0x04, uuidBytes); } /* * 将带有子类型0x04的二进制转换为UUID对象 * 请注意:没有检查子类型。 */ public static UUID fromStandardBinaryUUID(Binary binary) { long msb = 0; long lsb = 0; byte[] uuidBytes = binary.getData(); for (int i = 8; i < 16; i++) { lsb <<= 8; lsb |= uuidBytes[i] & 0xFFL; } for (int i = 0; i < 8; i++) { msb <<= 8; msb |= uuidBytes[i] & 0xFFL; } return new UUID(msb, lsb); }

3. 配置MongoDB GUI以处理带有子类型0x03的遗留UUID。只有少数MongoDB GUI工具允许指定如何处理遗留UUID字段。

MongoChef内置了对0x03和0x04子类型的支持。可以在属性对话框中配置行为,并从以下选项中选择:

  • 遗留.NET / C#编码:.NET/C#驱动程序使用的默认编码:以.NET应用程序的方式读取、存储和显示UUID
  • 遗留Java编码:Java驱动程序使用的默认编码:以Java应用程序的方式读取、存储和显示UUID
  • 遗留Python编码:Python驱动程序使用的默认编码:以Python应用程序的方式读取、存储和显示UUID
  • 编码/原始数据:不执行转换,以现有字节顺序读取、存储和显示数据
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485