MongoDB 的 ObjectId
是一种用于唯一标识文档的 12 字节标识符。它是 MongoDB 中最常见的默认主键类型(如果用户没有显式指定 _id
字段时)。每个 ObjectId
都是唯一的,并且包含了不同的信息,有助于在分布式环境中生成全球唯一的标识符。下面是 ObjectId
的底层实现细节:
ObjectId
是一个 12 字节的值,通常用 24 字符的十六进制字符串表示。它的结构如下:
| 4 字节 时间戳 | 5 字节 随机值 | 2 字节 增量计数器 | 1 字节 机器标识符 |
具体说明如下:
4 字节时间戳(从 Unix Epoch 开始的秒数,32-bit):表示生成 ObjectId
的时间,单位是秒。通过这个时间戳,我们可以推测 ObjectId
的生成时间。时间戳的前四个字节可以产生大约 136 年的唯一标识符。
5 字节随机值(48-bit 随机值):用于确保即使在不同的服务器或进程上生成的 ObjectId
也是唯一的。通常是由机器的网络接口卡(MAC 地址)或者随机数生成器来生成。
2 字节增量计数器(16-bit):这个计数器保证了即使在同一秒内生成多个 ObjectId
,它们也能保持唯一性。每当在同一秒内生成新的 ObjectId
时,增量计数器会自增。
1 字节机器标识符(8-bit):通常是机器的 ID 或者进程 ID,确保在分布式系统中不同的机器或进程能生成不重复的 ObjectId
。
MongoDB 的 ObjectId
在生成时并不是简单地随机生成的,而是依照上述的结构按顺序生成。具体步骤如下:
时间戳:首先获取当前时间戳(精确到秒),即从 Unix Epoch(1970-01-01 00:00:00 UTC)到当前时间的秒数。
机器标识符:MongoDB 会根据每台机器的特征(通常是机器的 MAC 地址,或者通过配置的其他方式生成机器 ID)生成一个唯一的标识符。这样即使在不同的机器上生成 ObjectId
,它们也能保证不同。
进程 ID 和增量计数器:在同一台机器上,多个进程可能同时生成 ObjectId
,因此需要一个进程 ID 来区分不同的进程,此外还需要一个计数器来保证即使在同一时间戳下,多个生成的 ObjectId
也是唯一的。这个计数器通常会在生成每个 ObjectId
时自增。
拼接形成 ObjectId
:最后,ObjectId
的各个部分按顺序拼接在一起,形成一个 12 字节的值。最终,这个 12 字节值可以转换成一个 24 字符的十六进制字符串。
全局唯一性:通过结合时间戳、机器标识符、进程 ID 和增量计数器,MongoDB 确保每个 ObjectId
在分布式环境中是唯一的。
排序特性:由于 ObjectId
中包含了时间戳信息,可以根据 ObjectId
值排序,从而使得按创建时间排序的查询更加高效。
高效生成:ObjectId
在生成时不依赖中央服务,因此生成速度非常快。它可以在客户端生成,并且不需要任何额外的同步机制。
减少碰撞的风险:通过加入多个唯一标识符(时间戳、机器 ID、进程 ID 和增量计数器),ObjectId
几乎没有碰撞的风险,即使在分布式环境下,碰撞的概率也极低。
在 MongoDB 中,ObjectId
是通过一个简单的算法自动生成的。大多数编程语言的 MongoDB 驱动程序都提供了生成 ObjectId
的方法。举例来说:
Python (PyMongo):
pythonfrom bson import ObjectId
obj_id = ObjectId()
print(obj_id)
JavaScript (Node.js):
javascriptconst { ObjectId } = require('mongodb');
const objId = new ObjectId();
console.log(objId);
每次调用 ObjectId()
时,都会根据当前时间、机器标识符等信息生成一个新的 ObjectId
。
ObjectId
中包含的时间戳是从 Unix Epoch(1970年1月1日)开始的秒数,因此你可以通过 ObjectId
反推出生成该 ID 的时间。
在 MongoDB 中,可以通过 ObjectId
提取其嵌入的时间戳。例如,使用 MongoDB 查询时:
javascriptconst objId = new ObjectId("507f1f77bcf86cd799439011");
console.log(objId.getTimestamp());
这将返回与该 ObjectId
关联的时间戳,帮助你得知该文档的创建时间。
MongoDB 的 ObjectId
是一个 12 字节的标识符,它包含了时间戳、机器标识符、进程 ID 和增量计数器,确保在分布式环境中生成全局唯一的 ID。它的设计不仅能有效避免冲突,还能提供按时间排序的特性,使得生成和存储过程非常高效。
本文作者:Eric
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!