当附件为重复文件时,如何启用“att_compression_level=9”并验证 SHA-1 去重?

解读

国内生产环境普遍把 CouchDB 当作“离线优先”数据总线,手机端、IoT 端频繁上传图片、日志等附件,导致同名不同内容同内容不同名的冗余。面试官抛出本题,核心想验证三点:

  1. 你是否知道 CouchDB 的附件是独立二进制 blob、与文档 JSON 分离存储;
  2. 能否把“压缩级别”与“去重”这两个看似无关的配置同时生效
  3. 能否用SHA-1 摘要在应用层或 Fauxton 层做二次校验,证明去重真实发生。

知识点

  1. att_compression_level 只在节点级配置文件 etc/local.ini[attachments] 段生效,范围 0–9,9 为最高压缩。
  2. 该参数仅对新建附件生效,已存附件不会回溯压缩;若需历史数据压缩,必须重写附件。
  3. CouchDB 内部以附件长度 + 摘要作为去重键,摘要算法默认 MD5,但可通过配置改为 SHA-1
  4. 摘要算法由 [attachments] hash_algorithm = sha 控制,sha 即 SHA-1(不是 SHA-256)。
  5. 验证去重时,可借助 GET /db/doc/att?name&att_encoding_info=true 返回的 encoded_lengthdigest 字段,比较两次上传的 digest 是否一致。

答案

步骤一:修改配置
每个节点etc/local.ini 追加:

[attachments]
compression_level = 9
hash_algorithm = sha

保存后 依次重启 所有节点,使配置生效。

步骤二:上传重复文件

  1. 准备一份测试文件 test.bin,记录本地 SHA-1:
    sha1sum test.bin → 假设得到 aabbcc...
  2. 第一次上传:
    curl -X PUT http://user:pass@127.0.0.1:5984/mydb/doc1/att?rev=1-xxx \ -H "Content-Type: application/octet-stream" --data-binary @test.bin
    返回 "rev": "2-..." 并记下 digest 字段,应为 sha1-aabbcc...
  3. 第二次上传不同名文档同一文件
    curl -X PUT http://user:pass@127.0.0.1:5984/mydb/doc2/att?rev=1-yyy \ -H "Content-Type: application/octet-stream" --data-binary @test.bin

步骤三:验证去重
查询附件元数据:
curl http://user:pass@127.0.0.1:5984/mydb/doc2/att?att_encoding_info=true
返回示例:

{
  "content_type": "application/octet-stream",
  "revpos": 2,
  "digest": "sha1-aabbcc...",
  "length": 102400,
  "encoded_length": 78888,
  "encoding": "gzip"
}

doc1doc2digest 完全一致,且 encoded_length < length,则证明同内容附件只存一份,且以最高级别压缩存储,SHA-1 去重生效。

拓展思考

  1. 如果业务需要SHA-256 而非 SHA-1,CouchDB 3.x 目前尚未内置,需在应用层先计算 sha256 作为附件名写入文档字段,再上传,实现“应用层去重”。
  2. 压缩级别 9 会显著增加 CPU,高并发写入场景建议先压测;国内云主机 2 vCPU 环境下,单节点 500 QPS 时 CPU 可飙至 70 %,可考虑折中设为 6。
  3. 若附件大于 4 MB,CouchDB 默认会分块存储,此时 encoded_length 是各块压缩后之和,验证去重时仍需比较顶级 digest,无需关心分块细节。