gridfs存储小文件时cpu占用异常高,因其本身未针对小文件优化,导致频繁的分块与合并操作增加cpu开销。

GridFS 存储小文件时 CPU 占用异常高
GridFS 本身不优化小文件(chunkSizeBytes=255KB,导致大量元数据操作和频繁的 fs.chunks 插入/查询。尤其在高并发写入场景下,MongoDB 的 BSON 解析、分块哈希、索引更新会吃掉大量 CPU。
- 若文件普遍 chunkSizeBytes 调大到
512KB或1MB(需权衡单 chunk 内存压力) - 避免用 GridFS 存纯文本或 JSON 配置——直接存 document 更快;GridFS 适合二进制流(图片、音频、PDF)
- 确保
fs.files._id和fs.chunks.files_id上有默认索引(Driver 通常自动建,但手动检查:运行db.fs.chunks.getIndexes()看是否存在{ files_id: 1, n: 1 })
Java Driver 写入大文件卡在 uploadFromStream()
不是网络慢,而是默认缓冲策略让 JVM 堆内存持续增长,尤其在未显式关闭流或未设置 maxConnectionsPerHost 时,容易触发 GC 暂停甚至 OOM。
- 务必用 try-with-resources 包裹
InputStream,否则uploadFromStream()可能持引用直到 GC - 调大
GridFSBucket的writeConcern级别(如WriteConcern.MAJORITY)前先确认副本集状态,否则写入会阻塞等待多数节点确认 - 对 >100MB 文件,改用
openUploadStream()+ 分段write(),便于控制 buffer 大小(例如每次write(byte[], 0, 8192))
find() + downloadToStream() 返回空或截断内容
常见于未正确处理异步流或忽略 GridFSFindIterable 的游标生命周期。MongoDB Java Driver 不会自动读取全部 chunks,下游流关太快就会丢数据。
- 必须调用
.first()获取单个GridFSFile,再传给downloadToStream();直接对未first()的游标调用会静默失败 - 确保目标
OutputStream(如FileOutputStream)未被提前close(),且磁盘有足够空间——GridFS 不校验写入端容量 - 如果文件名含 Unicode(如中文),查询时用
Filters.eq("filename", "报告.pdf"),别用正则或模糊匹配,避免编码不一致导致查不到
GridFS 在分片集群里查询变慢甚至超时
GridFS 的 fs.files 和 fs.chunks 默认不自动分片,所有 chunk 都落在一个 shard 上,成为热点。一旦该 shard 负载高,find() 或 delete() 就会排队甚至 CursorNotFound 错误。
- 必须手动对
fs.files按_id分片(推荐哈希分片:sh.shardCollection("mydb.fs.files", { "_id": "hashed" })) -
fs.chunks依赖files_id字段关联,所以也要按files_id分片(范围分片更合适:sh.shardCollection("mydb.fs.chunks", { "files_id": 1 })) - 分片后禁止使用
findOneAndDelete()删除文件——它无法跨分片原子执行,应拆成两步:先find()拿_id,再分别删fs.files和fs.chunks
GridFS 的性能瓶颈很少出在“怎么存”,而在于“谁来管分块、谁来保索引、谁来控流控”。尤其是分片集群里,fs.chunks.files_id 的分布不均比单机内存泄漏更难排查。











