GridFS下载慢八成因fs.chunks缺files_id索引,导致全表扫描;fs.files缺filename/uploadDate索引致查询慢;length排序失效因无对应索引;冗余索引反而拖慢性能,应按实际查询需求精准建索引。

GridFS下载慢八成是fs.chunks缺files_id索引
没这个索引,每次下载文件都要在fs.chunks集合里全表扫描——哪怕只是读一个 2MB 的图片,也要遍历几万甚至几十万条 chunk 记录。驱动层感知不到,但db.currentOp()里能看到secs_running飙升、CPU 持续 100%。
- 执行
db.fs.chunks.getIndexes(),确认输出中是否存在{ "files_id": 1 } - 如果缺失,立刻补:
db.fs.chunks.createIndex({ "files_id": 1 }) - 别依赖“驱动自动建”:只有用
GridFSBucket首次写入时才建;手动insert、迁移旧数据、或用废弃的GridFS类都不会触发 - 加完索引后,用
db.currentOp({ "ns": /^your_db_name\.fs\.chunks$/ })观察正在执行的 chunk 查询是否还卡在高secs_running
按filename或uploadDate查文件时响应极慢
因为fs.files集合默认只对_id建了唯一索引,其他字段如filename、uploadDate、metadata全是无索引裸奔状态。mongos 在分片集群里会广播请求到所有分片,查一次等于扫全部分片的fs.files。
- 补索引:
db.fs.files.createIndex({ "filename": 1, "uploadDate": -1 }) - 避免单独为
metadata.tag建单字段索引——除非该字段基数极高(比如每张图 tag 都不同),否则索引膨胀快、查询收益低 - 分片键被锁死为
_id,换不了;所以索引是唯一能落地的优化点
fs.files.length字段排序失效或返回空结果
想用聚合查最大 10 个文件,却得到空数组或乱序结果?大概率不是length字段不存在,而是fs.files集合根本没建索引,导致$sort无法利用 B-tree 加速,触发内存排序失败或超时降级。
- 先确认集合存在:
db.getCollectionNames().filter(n => n.startsWith('fs.')) - 检查字段:
db.fs.files.findOne({}, { projection: { length: 1 } })——length一定存在,它是 GridFS 写入时自动计算并写入的 - 建排序索引:
db.fs.files.createIndex({ length: -1 }),之后db.fs.files.aggregate([{$sort: {length: -1}}, {$limit: 10}])才能稳定跑出结果 - 注意:如果删过默认
_id索引(不推荐),length排序会更慢,因没了基础索引支撑
索引建错位置反而拖慢整体性能
不是所有索引都值得建。fs.chunks.files_id索引本身吃内存最多——千万级文件、平均 50 片,就是五亿条索引项。如果业务从不手动查chunks,这个索引纯属冗余负担。
- 确认是否真需要:
db.chunks.find({ files_id: ObjectId("...") })这种查询只出现在调试或修复场景,生产代码不该直查chunks - 可安全删除:
db.fs.chunks.dropIndex({ files_id: 1 })(注意老版本可能是{ files_id: 1, n: 1 }复合索引,得一起删) - 删前看缓存压力:
db.serverStatus().wiredTiger.cache里bytes currently in the cache占比超 90%,说明索引已挤占大量缓存空间 - 删完不影响
GridFSBucket.openDownloadStream()等正常 API 调用,只是不能手查分片
fs.chunks.files_id和fs.files.filename这两条索引,覆盖了 90% 的慢查问题,其余索引得按真实查询模式一条条验证必要性。


















