响应式MongoDB ObjectId较长为何_id索引快?手动设ID会变慢吗
你对MongoDB ObjectId的组成结构描述是准确的,之所以会产生和实际表现不符的预期,核心是忽略了ObjectId字段排布顺序带来的写入顺序优势,两个问题的具体解答如下:
为什么MongoDB的_id索引速度如此之快?
核心原因有三个:
- 默认
_id索引是集合创建时就自带的聚簇索引(WiredTiger存储引擎默认配置下),文档数据本身就直接挂在_id索引的B+树叶子节点上,不存在二级索引那样额外的回表查询、冗余索引条目写入的开销,插入文档的时候顺道就完成了索引条目写入,根本没有独立全量构建索引的那种排序、批量刷盘的额外成本。 - ObjectId的字段顺序天生保证了近似递增的特性:最靠前的4字节是Unix时间戳,新生成的ObjectId取值永远比之前生成的要大,插入B+树的时候几乎不用在树的中间位置找插入点,绝大多数写入都落在索引最右侧的叶子节点,极少触发索引页分裂,也几乎没有随机IO开销。你提到的5字节随机值、3字节计数器都排在时间戳字段后面,只会影响同一秒内生成的ObjectId排序,完全破坏不了整体的递增趋势。
- ObjectId是固定12字节的定长值,键值比较逻辑极快,没有变长字段的解析、逐字节长比对的额外开销,索引本身的内存占用也更低,缓存命中率自然更高。
手动设置随机唯一长整型值作为_id,是否会导致索引构建耗时变长?
要分情况看:
- 如果手动设置的长整型本身是趋势递增的(比如雪花算法生成的ID、其他数据库生成的自增ID),那性能和默认ObjectId基本没差,甚至因为长整型只占8字节,比12字节的ObjectId更省存储空间,性能还会略好一点,完全不会出现耗时变长的问题。
- 如果设置的是完全无顺序的纯随机长整型(比如真随机数生成的唯一值、哈希打散后的值),那确实会明显拉高索引维护的开销:随机ID会导致每次插入都要在B+树的任意位置找插入点,触发大量随机IO,还会频繁导致索引页分裂,等数据量超过内存能缓存的索引页大小后,缓存命中率会掉得很明显,写入性能会比用递增ID低30%到数倍不等,数据量越大差距越明显。不过小数据量场景下这个差异几乎感知不到,只有集合文档量到百万级以上才会逐步显现。
提个容易踩的误区:B+树索引插入性能的核心影响因素是键值的写入顺序,不是键值本身的长度,几个字节的长度差异对性能的影响,远小于乱序写入带来的随机IO和页分裂开销。
内容的提问来源于stack exchange,提问作者hadoobidoop
相关产品推荐
相关产品推荐

