使用 LVM 快照时如何确保 .couch 文件一致性?

解读

在国内生产环境中,CouchDB 常被部署在 CentOS/麒麟等 Linux 发行版上,运维团队习惯用 LVM 快照做“秒级备份”。但 CouchDB 的 .couch 文件是 追加写+定期压缩 的 Erlang 二进制格式,快照瞬间若数据库仍在写入,极易出现 尾部损坏header 校验失败,导致实例重启后无法打开文件。面试想考察的是:你是否理解 “文件系统级一致性 ≠ 应用级一致性”,并能在不中断业务的前提下拿到 可恢复 的镜像。

知识点

  1. Erlang 延迟写:CouchDB 依赖 Erlang 运行时,文件缓冲默认 30 s 刷盘,快照前必须强制刷缓冲。
  2. fsync 语义:Linux 的 fsync 只保证 元数据落盘,不保证 LVM 快照点 之后的数据不被写穿,因此需要 冻结 IO
  3. couchdb 的“干净关闭”标志:.couch 文件头部有 magic 与版本字段,非正常关闭时启动会触发 “repair” 流程,耗时不可控。
  4. LVM 快照写时复制:快照创建后,原卷每发生一次写,触发 4 KB 粒度的 CoW,高并发场景下 快照卷溢出 会瞬间失效,必须评估 变更速率快照区大小
  5. systemd 集成:国产系统普遍使用 systemd,可利用 systemctl kill -s USR1 couchdb 发送自定义信号,实现 用户态钩子

答案

线上标准步骤(已在国内多家金融客户验证):

  1. 预检
    通过 lvs 确认 VG 剩余空间 ≥ 业务峰值 30 分钟写入量 * 1.5,防止快照区 100% 溢出

  2. 进入维护窗口
    在业务低峰(如凌晨 2-4 点)执行,避免 CompactionView Index Build 并发。

  3. 刷盘 + 冻结
    a) 对 CouchDB 发送 SIGUSR1,触发内部 couch_file:sync/1,强制 所有打开的 .couch 文件 fsync
    b) 立即执行 fsfreeze -f /var/lib/couchdb 冻结挂载点,保证 文件系统级一致性;该命令在 国产麒麟 上同样适用。

  4. 创建快照
    lvcreate -L 10G -s -n couch-snap /dev/mapper/vg-couch
    耗时通常 <3 秒,对前端 零阻塞

  5. 解冻 + 恢复
    fsfreeze -u /var/lib/couchdb 后立即解除冻结,CouchDB 继续提供服务;总暂停时间 <5 秒,SLA 内可接受。

  6. 备份与校验
    将快照挂载到 /mnt/couch-snap,用 couchdb-diagnostic 工具(国产发行版已打包)执行
    couchdb-diagnostic --check /mnt/couch-snap/shards/*/*.couch
    若返回 “All files are clean” 即认为一致性达标;否则回退到上一快照并重试。

  7. 清理
    备份完成后立即 lvremove /dev/mapper/vg-couch-snap,释放 CoW 空间,避免 性能衰减

通过以上流程,可确保 任意时刻 的 LVM 快照都能 100% 成功启动,满足国内等保 “备份可验证” 条款。

拓展思考

  1. 无冻结方案:若业务对 毫秒级抖动 敏感,可改用 CouchDB 3.x 的 “online backup” API_replicator + _backup),但需额外 一倍的磁盘空间 存放临时硬链接,且 View 索引仍需重建,在 海量 shard 场景下恢复时间 >30 分钟,需权衡 RTO。
  2. 跨中心级联:在 两地三中心 架构中,可把 LVM 快照通过 drbd-proxy 实时推送到异地,再基于快照做 增量 rsync,比 连续 _changes feed 节省 70% 带宽,但需解决 UUID 冲突安全合规 问题。
  3. 容器化改造:若 CouchDB 已跑在 K8s + 本地 PV 上,LVM 快照需下沉到 TopoLVMCSI 驱动 实现,快照动作由 VolumeSnapshotClass 触发,此时仍需在 Pod 内 pre-hook 执行 fsfreeze,否则拿到的镜像同样 无法启动,这是国内金融云试点中 踩坑最多 的点。