GridFS真实瓶颈常在chunk分片、元数据锁或WiredTiger cache压力;需用mongostat与压测脚本交叉验证,确保chunkSizeBytes对齐、索引完整、流复用正确。

用 mongostat 和自定义压测脚本交叉验证实时吞吐
单纯看应用层写入 QPS 容易误判,GridFS 的真实瓶颈常在 chunk 分片、元数据锁或 WiredTiger cache 压力上。mongostat 能暴露底层操作分布,比如 netIn 高但 insert 低,说明网络或 driver 批处理没跟上;getmore 持续飙升则暗示 find 查询被阻塞。
实操建议:
- 启动
mongostat --host localhost:27017 -n 1,和压测脚本同时运行,盯住netIn、insert、query、getmore四列的秒级波动 - 压测脚本必须复用
MongoClient实例,避免每请求新建连接——否则connection accepted日志暴增,实际是连接池耗尽而非 GridFS 瓶颈 - 用
db.fs.chunks.stats()查看平均 chunk 大小,若远低于默认255KB,说明写入太碎,驱动未启用自动分块合并,吞吐必然受限
并发写入时 fs.createWriteStream 必须配 options.chunkSizeBytes
Node.js 的 mongodb 驱动默认 chunk 大小是 255 * 1024 字节,但高并发下若不显式传入,每个流会独立计算分块边界,导致大量小 chunk 写入,元数据膨胀快、WiredTiger page fault 频发。
常见错误现象:压测 QPS 上不去,mongostat 显示 insert 数值稳定在 200–400,但 netIn 很低,db.currentOp({ "secs_running": { "$gt": 1 } }) 返回大量等待 fs.chunks 插入的游标。
实操建议:
- 所有
fs.createWriteStream调用必须带{ chunkSizeBytes: 262144 }(即 256KB),和驱动默认值对齐,避免隐式重分块 - 不要用
stream.end(buffer)一次性写入大文件,改用stream.write(chunk)分批推送,控制单次 write 不超过 16MB(BSON 限制) - 若用
gridFSBucket.uploadFromStream,同样要传{ chunkSizeBytes: 262144 },否则驱动内部仍走默认逻辑
fs.files 和 fs.chunks 的索引缺失会导致并发插入卡顿
GridFS 依赖两个集合上的默认索引保证写入性能:fs.files._id_、fs.chunks.files_id_1_n_1。如果手动删过索引、或用 mongorestore 时加了 --noIndexRestore,并发插入会因唯一性校验或查询 files_id 变慢,表现为 insert 延迟陡升、connPoolStats 中 waitQueue 有堆积。
实操建议:
- 执行
db.fs.files.getIndexes()和db.fs.chunks.getIndexes(),确认存在{ files_id: 1, n: 1 }复合索引,且无重复或冗余索引干扰 - 若缺失,立即建索引:
db.fs.chunks.createIndex({ files_id: 1, n: 1 }, { unique: true }),注意加{ unique: true },否则 GridFS 写入可能出错 - 建索引期间禁止写入,否则会触发全表扫描——这不是 GridFS 问题,是索引缺失后 MongoDB 自身行为
用 db.runCommand({ "collStats": "fs.chunks" }) 判断是否 chunk 碎片化严重
极端并发下,如果写入速率波动大、或频繁中断重试,fs.chunks 会出现大量孤立 chunk(n 不连续)或小尺寸 chunk(length 远小于 chunkSizeBytes),导致读取时需多次 seek,拖累整体吞吐。
性能影响明显:即使 mongostat 显示 insert 正常,但下游调用 fs.openDownloadStream 时延迟飙升,db.currentOp() 中可见大量 find 操作卡在 fs.chunks 上。
实操建议:
- 压测后立即执行
db.runCommand({ "collStats": "fs.chunks" }),重点看avgObjSize是否接近chunkSizeBytes,以及count/size比值是否异常高(说明碎片多) - 不要直接删
fs.chunks,而是用fs.delete(fileId)清理无效文件,让驱动自动回收关联 chunk - 长期运行服务建议每周跑一次
db.fs.chunks.validate(),检查n序列连续性,不连续即需人工干预
真正卡住吞吐的往往不是单点函数,而是 fs.chunks 索引 + chunkSize + driver 流复用这三者没对齐。少一个,压测数据就不可信。










