如何删除过期?
解读
国内面试官问“如何删除过期”时,通常不是简单让你背一条 DELETE 语句,而是想验证三件事:
- 你是否理解 CouchDB 无内置 TTL 的现实;
- 你是否能把“过期”抽象成“业务时间戳 + 视图过滤”;
- 你是否能在高并发、大容量、离线优先场景下给出可灰度、可回滚、对同步友好的工程方案。
一句话:要证明你能在生产环境安全、可控、可观测地清理过期文档,而不是把数据库打挂。
知识点
- CouchDB 无 TTL 机制,过期逻辑必须应用层或外围脚本实现。
- 文档唯一版本号 _rev 决定并发安全,删除操作必须携带最新 _rev。
- 删除本质是写入 _deleted:true 的墓碑(tombstone),墓碑会参与同步,不可物理抹除。
- 视图(map/reduce)可过滤过期文档,但视图只是索引,不会回收空间。
- _purge 可物理清除墓碑,但不可逆、不同步,国内合规场景慎用。
- _changes feed 支持按时间戳回溯,可做增量清理调度。
- 国内云厂商托管版(如腾讯云 TDSQL-CouchDB)禁止直接访问底层文件,_purge 需提工单审批。
- 离线优先场景下,移动端可能长时间不同步,清理前必须确认所有副本已收敛。
答案
给面试官一个“三步走”生产级方案,每步都带灰度与观测:
第一步:业务字段标记过期
在每条文档里显式写入 "expireAt": "2025-06-01T00:00:00Z"(ISO8601,UTC),确保字段有统一时区规范。
第二步:视图过滤 + 增量扫描
新建设计文档 "_design/expiration",视图函数:
function (doc) {
if (doc.expireAt && new Date(doc.expireAt) <= new Date()) {
emit(doc._id, null);
}
}
每天凌晨通过 /_view/expiration 分页拉取过期 ID 列表,批量 HEAD 请求获取最新 _rev,再按 100 条/批次发送 POST /{db}/_bulk_docs,payload 为:
{"docs":[{"_id":"xxx","_rev":"yyy","_deleted":true}]}
返回结果里统计 failures 与 conflicts,写入 Prometheus,告警阈值 1%。
第三步:空间回收与合规
- 墓碑保留 7 天后再执行 _purge;
- _purge 前必须
- 确认 /_active_tasks 无复制任务;
- 用 /_scheduler/docs 检查所有节点 checkpoint 时间 > 墓碑写入时间;
- _purge 操作走国内云厂商工单,双人复核,审计日志落盘 3 年;
- 最后触发 /_compact 回收空间,监控磁盘下降速率 < 100 MB/min,防止 IO 打满。
拓展思考
- 如果业务要求 毫秒级过期,CouchDB 并不适合,可引入 Redis + CouchDB 双写,Redis 做滑动窗口,CouchDB 做最终归档。
- 多主复制场景下,不同节点时钟漂移可能导致“提前过期”,需用 NTP + 最大时钟漂移容忍值(如 5 秒) 二次校验。
- 国内等保 2.0 要求“数据可恢复”,**_purge 前务必做 逻辑备份(couch-dump 到 OSS 冷存),备份保留周期 180 天。
- 对 TB 级单库,可改用 按时间分库(db-2025q2) 策略,到期直接 删除整个库,避免墓碑膨胀;删除库前用 /_membership 确认集群无重平衡任务。