首页/数据/NoSQL 速查/ObjectId 结构与生成时间
MongoDB99 条

ObjectId 结构与生成时间

默认 _id 是 12 字节 ObjectId:4 字节秒级时间戳 + 5 字节随机值 + 3 字节递增计数器;可反解时间戳做粗略排序参考。

NoSQL 速查数据更新于 2026-10-13

ObjectId 是什么

ObjectId 是 MongoDB 默认的 _id 类型,共 12 字节:前 4 字节是创建时刻的秒级时间戳,中间 5 字节是进程随机值,最后 3 字节是以随机数起始的递增计数器。字符串形式是 24 位十六进制。它由客户端本地生成,插入前不需要向服务端申请主键,省掉一次往返;末尾递增的特性让同一进程的插入近似顺序写,避免像随机 UUID 那样造成索引页频繁分裂。

基本信息

长度12 字节,表示为 24 位十六进制字符串
组成4 字节时间戳 + 5 字节随机值 + 3 字节递增计数器
反解时间id.getTimestamp()
生成方式new ObjectId() 或 ObjectId("24 位十六进制")
唯一性客户端生成,碰撞概率极低但需唯一索引兜底

典型使用场景

  • 作为默认主键:绝大多数集合直接使用,无需额外声明
  • 粗粒度按时间排序:_id 升序约等于插入顺序,可用于日志类集合浏览
  • 反查写入时间:只拿到 _id 时用 getTimestamp() 还原创建时刻
  • 键集分页:用 _id 大于上一页最后一条作为翻页条件,比 skip 高效
  • 接口入参转换:外部传入的 24 位字符串要转成 ObjectId 再查询

生成与反解

生成与反解
const id = new ObjectId()      // 客户端生成,不依赖服务端
id.getTimestamp()              // 反解创建时间,只有秒级精度
id.toString()                  // 24 位十六进制,跨系统传参用这个形式

ObjectId("652b1c9a8f1b2c3d4e5f6a7b").getTimestamp()

// 键集分页:以上一页最后一条的 _id 为游标
db.orders.find({ _id: { $gt: ObjectId("652b1c9a8f1b2c3d4e5f6a7b") } })
  .sort({ _id: 1 }).limit(20)

结构逐段读法

区段字节含义
时间戳4自 Unix 纪元起的秒数,不精确到毫秒
随机值5每个客户端进程启动时生成一次,避免不同机器撞号
递增计数器3随机起始值,同进程内每次生成加一
字符串形式24十六进制字符,按字节序可直接比较大小
也可以拿业务键(订单号、设备号)当 _id,好处是天然唯一可读,代价是必须自己保证它永不变化;一旦 _id 改了,所有引用它的外部系统都要跟着改。
同一秒内的 ObjectId 顺序由计数器决定,跨进程比较不出严格先后,别把它当成全局递增序列号使用。

易错点

  • ObjectId 内嵌时间只有秒级精度,不能当业务时间戳使用,同一秒内的多条数据分不出先后
  • 把 ObjectId 当自增主键用:它按客户端分布生成,跨机器的时间戳部分会交错
  • 查询时把 _id 当普通字符串处理,类型不匹配会查不到文档,入参必须显式转换
  • 导入外部数据时自行指定 _id,要与业务唯一键的约束一起核对,避免出现两套主键语义

数据来源与参考

  • MongoDB Manual: ObjectId
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌