mongodb 中必须了解的关于 ObjectId 的知识
ObjectId 的构成
ObjectId 的值由 12 个字节组成,其中:
- 4个字节表示时间戳(自 Unix 纪元以来的秒数),记录创建时间;
- 3个字节表示机器标识符,保证不同主机产生不同的 ObjectId 值;
- 2个字节表示进程 ID,保证在同一台主机不同 MongoDB 进程产生不同的 ObjectId 值;
- 3个字节表示自增计数器(以随机值开头),保证同一主机同一进程同一秒内产生 ObjectId 的唯一性。
ObjectId = 时间戳(4字节) + 机器标识码(3字节) + 进程 ID(2字节) + 计数器(3字节)
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
---|---|---|---|---|---|---|---|---|---|---|---|
时间戳 | 机器标识码 | 进程 ID | 计数器 | ||||||||
机器随机数 |
每个字节的值为 1~254,ObjectId 转换成字符串后,是用十六进制表示,所以,字符串型 ObjectId 有 24 个字符。
ObjectId 的值无法保证生成顺序
ObjectId 前 4 个字节存的是时间戳,而时间是递增的,所以 ObjectId 总体保证递增的顺序。
存储的时间戳只精确到秒,在同一台机器不同的 MongoDB 进程,同一秒内生成的 ObjectId,进程 ID 小的会排在大的前面。存在这种情况,进程 ID 大 的先生成 ObjectId,但还是会排在进程 ID 小的后面。所以 ObjectId 递增不是绝对的。
ObjectId 在一秒内生成的数量上限
3 个字节所能表达的最大的整数:\(2^{24}-1\)。所以一个 MongoDB 进程,在一秒内最多能生成 \(2^{24}-1\) 个ObjectId。
从目前机器的性能来看,要超过这个限制几乎是不可能的。
ObjectId 的唯一性
ObjectId 近似唯一,理论上会出现很小概率 \(\frac{1}{2^{24}-1}\) 的重复情况,这取决于 MongoDB 驱动实现 ObjectId 方式。
以 C# 官方驱动来说,构成 ObjectId 的计数器,C# 使用了 Interlocked.Increment 实现,保证了同一MongoDB 进程在同一秒内生成的多个 ObjectId 的计数器是累加的,从而保证了生成的 ObjectId 是唯一的。
不过,有些版本的驱动是使用了随机数作为计数器,这种情况下并不能保证生成的 ObjectId 是唯一的。
所以,除非你使用的是一个非常老的版本,或者很小众的驱动,否则都不需要为重复的 ObjectId 担心。
可以使用表达查询 ObjectId
1 | db.comments.find({_id: {$gt: ObjectId("5272e0f00000000000000000")}}) |
ObjectId 无法修改
文档一旦写入到集合中,则其 _id
将不允许改变。如果需要更改某个文档的
_id
,可以将文档删除后重新添加到集合中。
参考
- https://www.jianshu.com/p/7c4bfa516acf
- ObjectId — MongoDB Manual
- MongoDB ObjectId to Timestamp Converter