MongoDB中用ObjectId存储关联ID是否影响查询性能?
关联ID存储为ObjectId vs 字符串:性能差异及测试方案
性能核心差异
- 存储成本:ObjectId是12字节的二进制类型,对应的字符串是24字节(每个字节转成两个十六进制字符)。数据量越大,ObjectId的存储空间优势越明显,直接影响磁盘IO和内存占用效率。
- 查询效率:
- 如果存储的是ObjectId但用字符串查询,MongoDB会做隐式类型转换,这会导致索引失效,查询退化为全集合扫描,大数据量下性能暴跌。
- 类型匹配时(ObjectId查ObjectId、字符串查字符串),只要字段有索引,MongoDB可以直接通过索引定位文档,查询速度差距在百万级数据量下会非常显著。
- Laravel ORM适配:Laravel的MongoDB扩展(如jenssegers/laravel-mongodb)默认以ObjectId作为关联ID的标准类型,字符串存储需要额外做类型转换,既增加代码复杂度,也会在ORM层面带来微小的性能损耗。
实测方案(Laravel+MongoDB场景)
1. 准备测试数据集
- 创建
users和comments两个集合,生成10万条用户数据,每个用户关联10条评论,最终得到100万条评论数据。 - 拆分两组测试:一组
comments的userId存字符串,另一组存ObjectId。
2. 关键查询场景测试
- 单用户评论查询:对比两种存储方式的查询耗时,同时用MongoDB的
explain()分析查询计划// 字符串ID查询 $start = microtime(true); $comments = Comment::where('userId', '5ff7fbc93e2c5a4462b1e612')->get(); echo "字符串ID查询耗时:" . (microtime(true) - $start) . "秒\n"; // ObjectId查询 $start = microtime(true); $comments = Comment::where('userId', new MongoDB\BSON\ObjectId('5ff7fbc93e2c5a4462b1e612'))->get(); echo "ObjectId查询耗时:" . (microtime(true) - $start) . "秒\n"; - 索引有效性验证:给
userId字段创建索引后,执行以下命令查看执行细节:
重点看// 字符串存储集合 db.comments_string.explain("executionStats").find({userId: "5ff7fbc93e2c5a4462b1e612"}) // ObjectId存储集合 db.comments_objectid.explain("executionStats").find({userId: ObjectId("5ff7fbc93e2c5a4462b1e612")})executionStats.totalDocsExamined:用索引时该数值等于匹配的文档数;全表扫描时等于集合总文档数。
3. 存储占用对比
执行db.comments.stats()查看两个测试集合的dataSize和storageSize指标,直观对比ObjectId的存储缩减比例。
结论
优先选择存储为ObjectId类型,既符合MongoDB设计规范,又能保证索引有效利用,避免隐式类型转换的性能损耗,同时和Laravel的MongoDB扩展兼容性更好。
内容的提问来源于stack exchange,提问作者Marcos Nobrega
相关产品推荐
相关产品推荐

