gridfs是mongodb官方定义的文件分块存储协议,用fs.files存元数据、fs.chunks存二进制块(默认255kb),非独立服务或插件;不支持单文档超16mb,须用gridfsbucket等驱动封装接口操作,禁用直接insertone写大文件。

直接用 insertOne 往集合里写一个 20MB 的视频?会报 Document too large 错误——MongoDB 的 BSON 文档硬限制是 16MB,跨不过去。必须用 GridFS。
GridFS 是什么,不是什么
GridFS 不是独立服务,也不是插件,它是 MongoDB 官方定义的一套文件分块存储协议,所有官方驱动(Python、Node.js、Java 等)都原生实现了它。它不改变 MongoDB 本身,只是约定用两个集合:fs.files 存元数据,fs.chunks 存二进制块,默认块大小 255KB(注意:不是 256KB,部分文档写错,实际是 261120 字节)。
常见误解:
- 认为 GridFS 是“MongoDB 的文件系统替代品”——它只是逻辑抽象,底层仍是普通文档,没引入新存储引擎
- 以为必须手写
fs.files和fs.chunks插入逻辑——99% 场景应直接用驱动封装好的GridFSBucket或GridFSStreamAPI - 混淆
mongofiles工具和生产代码——mongofiles仅适合调试或一次性导入,不支持并发写、流式上传、自定义 metadata 过滤等关键能力
Node.js 中用 GridFSBucket 写入大文件
用官方 MongoDB Node.js 驱动(v6+)时,GridFSBucket 是首选接口,比老版 GridStore 更稳定、API 更清晰。
关键点:
-
chunkSizeBytes可调,但别乱改:设太小(如 4KB)会导致fs.chunks文档爆炸,索引膨胀;设太大(如 2MB)削弱流式读优势,且单块仍受 16MB 限制 - 上传时传入的
filename必须是字符串,不能是路径(如/tmp/video.mp4),否则下载时可能解析异常 - metadata 字段可存任意 JSON 兼容对象,比如
{ userId: "u_123", tags: ["raw", "4k"] },后续可用find({ "metadata.userId": "u_123" })查询 - 务必
awaituploadStream.end(),否则文件写入不完整,fs.files里有记录但fs.chunks缺块
简例:
const { GridFSBucket } = require('mongodb');
const bucket = new GridFSBucket(db, { bucketName: 'media' });
const uploadStream = bucket.openUploadStream('concert.mp4', {
chunkSizeBytes: 261120,
metadata: { uploader: 'admin', durationSec: 3280 }
});
fs.createReadStream('./concert.mp4').pipe(uploadStream);
await finished(uploadStream); // 等待流结束
查询和下载大文件时的性能陷阱
GridFS 查询本质是两阶段:先查 fs.files 拿 _id,再按 files_id 查 fs.chunks。容易卡在三个地方:
- 没给
fs.files建复合索引:至少要{ filename: 1, uploadDate: -1 },否则find({ filename: "xxx" })全表扫 - 用
findOne({ _id: ... })下载却没在fs.chunks上建{ files_id: 1, n: 1 }索引——这是 GridFS 必备索引,缺失会导致块读取顺序混乱甚至超时 - 并发下载多个大文件时,未控制
bucket.openDownloadStream()的并发数,耗尽连接池或触发 WiredTiger 内存压力 - 误用
toArray()加载整个文件到内存:对 500MB 视频调stream.toArray()直接 OOM,必须用管道(pipe)或分片读取
分片集群下 GridFS 的隐性约束
GridFS 在分片集群上能自动把 fs.chunks 分布到不同 shard,但有两个硬限制常被忽略:
-
fs.files集合**不能分片**,必须是未分片集合(shard key 为{ _id: "hashed" }也不行),否则put会失败 -
fs.chunks默认以files_id为分片键,这意味着同一文件的所有块必然落在同一个 shard 上——无法靠分片提升单文件吞吐,只能靠多文件并行摊薄负载 - 如果启用了 zone sharding,需确保
fs.chunks的分片键范围与 zone 范围对齐,否则部分块可能路由失败
真正需要横向扩展大文件 IO 的场景,GridFS 不是银弹;此时该评估对象存储(如 S3 + MongoDB 存元数据)是否更合适。











