如何校验 MD5?
解读
在国内 CouchDB 面试中,面试官问“如何校验 MD5”并不是想听你背诵 Linux 命令,而是考察三层能力:
- 你是否理解 CouchDB 对 附件完整性 的依赖——CouchDB 把每个附件的 MD5 摘要 作为
ETag返回,并在复制时用它做 增量差异比对; - 你是否能在 离线优先、弱网、移动同步 场景下,用最小成本验证文件未被篡改或损坏;
- 你是否知道 国内合规要求(等保 2.0、国密改造)对摘要算法的潜在影响,能否给出可落地的替代或加固方案。
因此,回答必须围绕“CouchDB 原生机制 → 客户端校验 → 运维闭环 → 合规演进”展开,体现端到端的工程思维。
知识点
- CouchDB 附件的 content-md5 头部与 ETag 语义一致,均存放 Base64 编码的 MD5 值。
- 增量复制 时,源库会在
multipart/related响应里携带Content-MD5,目标库会 二次计算 并比对,失败则回滚该附件。 - 国内移动网络 高丢包、高延迟,客户端必须先做 本地 MD5 预校验,再决定是否上传,节省 30%~50% 流量。
- 等保 2.0 对 数据完整性 有检查点,若未来需改用 SM3,CouchDB 可通过 自定义中间层 在
before_doc_update钩子中二次摘要,实现 双摘要并存,平滑过渡。 - 大文件场景下,流式 MD5 比一次性加载内存更安全,Node.js 使用
crypto.createHash('md5'),Java 使用DigestInputStream,防止 OOM 导致容器重启。
答案
CouchDB 内置了附件级 MD5 校验,分为 服务端、客户端、运维 三条线:
-
服务端:
- 上传附件时,CouchDB 自动计算 MD5 并写入 ETag;
- 复制同步阶段,目标节点会 二次计算 收到的字节流,与
Content-MD5头部比对,不一致则 回滚该附件 并记录attachment_verify_failure到couch.log,保证 最终一致性。
-
客户端(以国内最常见的 React-Native + PouchDB 为例):
- 离线场景先本地计算
SparkMD5,与待上传文件的摘要比对,防止 重复上传; - 收到 CouchDB 返回的 201 响应后,再次比对 ETag 与本地值,若不一致立即 触发重试 并上报
Sentry,避免用户看到“假成功”。
- 离线场景先本地计算
-
运维闭环:
- 每天凌晨用 couch-dump 导出附件清单,通过
curl -I批量抓取 ETag,与对象存储(如 阿里云 OSS)的Content-MD5交叉校验,差异超过 1‰ 自动发 钉钉告警; - 若发现不一致,立即触发
couchdb-compact并对比before/after摘要,确认是 位翻转 还是 逻辑损坏,再决定是 单节点恢复 还是 全集群重同步。
- 每天凌晨用 couch-dump 导出附件清单,通过
一句话总结:利用 CouchDB 原生 ETag,结合客户端预校验与运维交叉抽检,形成“上传-复制-巡检”三级 MD5 闭环,既满足高可用,又符合国内等保合规。
拓展思考
- 当单附件大于 500 MB 时,MD5 计算会 阻塞 Erlang 调度器,可在
local.ini里开启{attachment_buffer_size, 1048576},并改用 NIF 异步摘要,将 CPU 抢占降到 5% 以下。 - 国内金融客户已要求 SM3 摘要,可在 反向代理层(OpenResty) 用
lua-resty-sm3计算双摘要,回写自定义头部X-SM3,CouchDB 仍存 MD5,实现 双轨制,无需改源码即可过审。 - 未来 CouchDB 4.x 若引入 分片附件,MD5 校验粒度将从“整文件”变为 分片级 Merkle Tree,需要提前设计 分片摘要缓存 策略,避免 二次读取 IO 翻倍。