gridfs实际磁盘占用=fs.chunks.storagesize+fs.files.storagesize,非length求和;需在对应库执行collstats,注意分片、索引冗余及采集权限。

直接用 collStats 查 fs.chunks 和 fs.files 的 storageSize
GridFS 实际磁盘占用 = fs.chunks 集合的 storageSize + fs.files 集合的 storageSize,不是 $sum: "$length"。前者是 WiredTiger 文件系统里真实占的磁盘块,后者只是逻辑字节数(还可能不准)。
- 在对应数据库下执行:
db.runCommand({ "collStats": "fs.chunks", "scale": 1024 })和db.runCommand({ "collStats": "fs.files", "scale": 1024 }),返回里的storageSize字段单位是 KB(因加了scale: 1024) - 注意:必须在 GridFS 所在数据库上下文中运行,比如你的 bucket 是默认的
fs,那就要先use myapp_db再执行命令,不能在admin库里硬调 -
storageSize明显大于size是正常现象——WiredTiger 有 padding、碎片、删除不立即回收空间;别误以为统计错了 - 如果集合启用了分片,
collStats只返回当前 shard 的局部值,得连到每个 shard 单独查,或用mongos执行分布式聚合(MongoDB 4.4+ 支持)
为什么不能只对 fs.files.length 求和?
因为 length 是逻辑大小,且数据质量不可靠:文件删了但 fs.files 文档残留、旧驱动写入时漏填 length、字段类型为 null 或字符串都可能发生。
- 即使你加了
{$match: { length: {$type: "long"} }}过滤,也只反映“理论上该有多少字节”,不等于磁盘实际占多少 - 一个 1MB 文件,
length是 1048576,但fs.chunks的storageSize可能是 1.1MB——chunk 对齐、BSON 开销、索引页都会吃空间 - 生产环境建议给
{ length: 1 }建稀疏索引:db.fs.files.createIndex({ length: 1 }, { sparse: true }),避免聚合扫描全表
批量统计或监控时,得自己写采集脚本
mongodb_exporter 不暴露 fs.* 集合的 storageSize,它只返回数据库级指标(如 mongodb_mongod_storage_data_size_bytes),跟 GridFS 无关。
- 必须绕过 exporter,用 Python/Shell 定期连接 MongoDB,执行两个
collStats命令,提取storageSize,拼成 Prometheus 格式,例如:gridfs_storage_bytes{collection="fs.chunks"} 2485671936gridfs_storage_bytes{collection="fs.files"} 12456960 - 采集账号需有
read权限到fs.*集合,不能只靠readAnyDatabase——某些部署会拒绝跨库读取 - scrape interval 别设太密(≥30s),否则频繁跑
collStats会给主节点带来额外压力
容易被忽略的细节:索引本身也占磁盘和内存
GridFS 的 fs.chunks.files_id 索引(不是 _id)才是真正的存储大户——千万级图片,每张切 50 片,就是 5 亿条索引项,光这个索引就可能占几个 GB。
- 确认是否真需要通过
files_id查 chunk:日常读写走 GridFS API,根本不会直查fs.chunks,那这个索引大概率是冗余的 - 可安全删除:
db.fs.chunks.dropIndex({ files_id: 1 })(先db.fs.chunks.getIndexes()看清索引名,有些版本是复合索引{ files_id: 1, n: 1 }) - 删完后
collStats返回的storageSize会明显下降,而且wiredTiger.cache压力也会缓解——这点常被运维忽略











