必须分别校验GridFS的files和chunks集合:files需比对_id、filename、length、uploadDate、md5;chunks需按files_id分组验证chunk数量、n序号连续性及data的SHA256;禁用dbHash和count(),优先流式计算文件级哈希并比对ContentType与metadata。

直接比对 GridFS 的 files 和 chunks 集合行数与哈希值
GridFS 不是单文档存储,而是拆成 files(元数据)和 chunks(二进制块)两个集合。只比文档总数或用 dbHash 会漏掉块级差异——比如某文件的某个 chunk 写丢了但 files 记录还在,dbHash 依然通过。必须分开校验:
-
files集合:确认每个文件的_id、filename、length、uploadDate、md5字段完全一致;md5是 GridFS 自动计算的,迁移工具若未保留该字段(如某些自定义脚本忽略它),一致性就失去基础依据 -
chunks集合:按files_id分组,核对每组 chunk 数量、每个 chunk 的n(序号)是否连续、data字段的 SHA256 是否匹配;注意默认块大小是 255 KiB,但最后一块可能更小,不能简单按“总长度 ÷ 块大小”反推 chunk 数 - 别依赖
count():MongoDB 的count()在分片集群或带读写延迟的副本集中可能不准,要用db.collection.estimatedDocumentCount()或聚合管道$group+$sum
用 mongofiles 导出再比对文件内容
mongofiles 是 MongoDB 官方提供的 GridFS 命令行工具,能绕过驱动层直接操作底层集合,适合做最终一致性兜底验证。但它不支持分片集群,且要求源/目标集群都启用 enableMajorityReadConcern: true(否则可能读到脏数据):
- 从源库导出所有文件:
mongofiles -h src_host:port -d dbname --username user --password pass list > src_list.txt,再逐个get到临时目录 - 从目标库同样操作,得到另一套文件
- 用
diff -r src_dir dest_dir比对;注意:如果文件名含特殊字符或路径过长,mongofiles get可能截断或报错,建议先用list输出 JSON 格式(加--json),解析后按_id而非filename下载,避免命名冲突 - 大文件(>1GB)慎用此法:本地磁盘可能撑不住,且网络传输引入额外校验失败风险
在 Go 中用 gridfs 包流式计算文件级哈希
如果你的迁移流程本身用 Go 实现(比如基于 mongo-go-driver/x/exp/gridfs),最轻量且可靠的方式是在迁移时或迁移后,对每个文件流式读取并计算哈希——既不加载全量到内存,又能拿到端到端校验值:
- 上传前:用
io.MultiWriter同时写入uploadStream和sha256.New(),得到原始文件哈希 - 迁移后:用
b.OpenDownloadStream(id)获取gridfs.File,再用io.Copy(io.Discard, file)流式读取并更新哈希器;注意必须调用file.Close(),否则底层 cursor 不释放 - 关键点:
gridfs.File.Size()返回的是逻辑文件大小,不是 chunks 总字节数;实际读取时若遇到缺失 chunk,io.Copy会返回EOF且字节数小于Size(),这就是一致性破绽 - 别跳过
ContentType和metadata字段比对:有些业务依赖这些字段做路由或鉴权,它们存在files文档里,但mongofiles默认不导出
避开 dbHash 和仅查文档数这类“假阳性”验证
dbHash 命令看似省事,但它在 GridFS 场景下极易给出错误结论。根本原因是:dbHash 对集合做整体哈希,不感知 files 和 chunks 之间的逻辑关联。哪怕一个文件的最后一个 chunk 写丢了,只要 files 文档没删,dbHash 仍可能一致。
-
dbHash会锁库,且明确不支持分片集群——你在生产环境跑一次,等于主动制造一次写入中断 - 只比
db.collection.count()更危险:files少一条可能意味着整个文件丢失,chunks少几条可能只是部分损坏,但两者 count 都可能“碰巧相等” - 如果用了自定义 bucket 名(比如
mybucket.files),验证脚本必须显式指定,不能硬编码fs.files;mongosync的嵌入式验证器默认只检查fsbucket,其他 bucket 得自己写逻辑
真正难的不是技术动作,而是确认“哪些字段必须一致”——比如 uploadDate 是否允许毫秒级偏差、md5 字段是否被迁移工具重算过、metadata 里业务自定义字段是否全量同步。这些细节不提前约定,校验就只是自我安慰。











