密钥轮换后,如何批量 re-encrypt 历史文档而不停机?
解读
在国内金融、政务、医疗等合规场景,国密算法轮换周期≤1年已成硬性要求。CouchDB 本身只负责存储密文,真正的加解密逻辑在业务层或中间层完成,因此“不停机 re-encrypt”的核心是:在持续读写的前提下,完成全量历史文档的加密版本升级,并确保新旧密钥混用期间的数据一致性、可用性与可回滚性。面试官想验证的是:你是否理解 CouchDB 的 MVCC、复制、视图机制,能否设计一套灰度可控、可观测、可回滚的在线批量轮换方案,而不是简单回答“写个脚本遍历”。
知识点
- MVCC 与 _rev 机制:CouchDB 每次更新生成新 _rev,旧版仍可读,天然支持“双写双读”灰度。
- _changes feed:支持
since=now持续监听,用于实时捕获增量。 - _design/update 函数:可在写路径实时 re-encrypt,实现“写时旋转”。
- _bulk_docs:批量提交,配合
all_or_nothing=false可部分成功,失败重试不阻塞。 - _replicator:内部复制任务可带自定义 filter,用于灰度切流。
- 国密合规:SM4 轮换需保留**密钥版本号(kv)**字段,密文前缀标识新旧密钥。
- 零停机指标:RPO=0(不丢写)、RTO<30s(切密钥窗口)、回滚时间<5min。
答案
整体采用“双密钥双读 + 增量写时旋转 + 后台批量追赶 + 灰度切换”四阶段方案,全程不锁库、不中断业务。
阶段 1:前置准备
- 在应用层引入密钥路由器:根据文档中的
kv字段自动选择解密密钥,支持同时加载新旧两把 SM4 密钥。 - 新建
_design/crypto视图,输出_id与kv,用于后续批量扫描。 - 打开用户认证 + db-admin 强制 HTTPS,防止中间人降级。
阶段 2:增量写时旋转(在线)
- 在 update_handler(或应用 DAO)中统一拦截写请求:
- 若传入明文,用新密钥加密,写入
kv=new。 - 若传入密文且
kv=old,立即用新密钥 re-encrypt 后再写,保证增量文档永远使用最新密钥。
- 若传入明文,用新密钥加密,写入
- 开启
_changes?feed=continuous&since=now监听,实时统计“写时旋转”成功率,低于 99.9% 自动告警。
阶段 3:后台批量追赶(异步)
- 使用
_design/crypto视图分页扫描kv=old的文档,按 1 000 条/批提交_bulk_docs。 - 每批携带
new_edits=false并指定最新_rev,避免冲突;若冲突则延迟 1s 重试,重试 3 次后落入死信队列,人工复核。 - 任务部署在 K8s CronJob,限流 50% CPU,通过
_active_tasks接口实时上报进度;全量 1 亿条文档可在 4h 内完成(8 节点,每节点 8 并发)。
阶段 4:灰度切换与清理
- 当追赶进度 ≥99.5% 且持续 30min 无
kv=old增量时,启动灰度切换:- 修改密钥路由器默认加密密钥为新密钥;
- 通过
_replicator建立灰度任务,把 5% 流量复制到预发集群,验证解密正确性; - 无异常后全量切换,老密钥保留 72h 用于应急回滚。
- 切换完成后,删除视图、停批任务、清理死信,出具《密钥轮换报告》留存备审。
通过以上四阶段,业务读写 QPS 零抖动,轮换过程可观测、可回滚,满足国内等保 3 级与国密合规要求。
拓展思考
- 如果集群跨两地三中心,_changes 顺序在异地复制时可能出现延迟,如何确保“增量写时旋转”不会重复加密?
可在 update_handler 里加分布式锁(Redis Redlock),或利用_rev前缀做幂等令牌。 - 当单个文档 ≥20 MB(附件)时,
_bulk_docs压力陡增,可改用CouchDB 2.3+ 的 _attachments 流式加密接口,边读边写,降低内存峰值。 - 未来若引入硬件加密机(HSM),密钥路由器需支持 PKCS#11 动态加载,同时把批量追赶任务拆成MapReduce 作业,在加密机池里并行完成,整体时间可缩短 60%。