如何验证数据?
解读
在国内 CouchDB 面试中,面试官问“如何验证数据”并不是想听“写个 if 判断”这种通用答案,而是想确认候选人是否真正落地过 分布式、离线优先、多主复制 场景下的数据质量保障。
核心考点有三层:
- 单文档层面:JSON 结构、业务规则、必填字段、值域、格式。
- 跨文档层面:关联一致性、业务唯一性、状态机合法性。
- 复制与冲突层面:因 CouchDB 多主复制必然产生冲突,验证必须覆盖 冲突检测、冲突解决、最终一致性 全过程。
回答时要体现“离线优先”思维:客户端先写本地,再异步同步,验证逻辑必须 在客户端、服务端、同步链路 三段同时生效,否则上线后会出现“手机端看数据合法,同步后全库污染”的生产事故。
知识点
-
validate_doc_update
CouchDB 原生在设计文档里提供 validate_doc_update 函数,用 JavaScript 写,每次写入触发,可抛 forbidden/unauthorized 错误。国内项目常配合 role-based 权限 做“同一条数据,普通用户只能改自己的字段,管理员可改全部字段”的细粒度校验。 -
Erlang 前置钩子(couch_epi 插件)
对性能要求高的场景(如秒杀库存),可在 Erlang 层写 pre-commit hook,在文档落盘前做 内存级强校验,比 JavaScript 快一个数量级,国内头部电商的库存中心采用此方案。 -
JSON Schema 校验
若团队前端用 Node.js,可直接复用 JSON Schema 文件;CouchDB 自身不内建,但可在 validate_doc_update 里手动调用 ajv 等库,实现“一套 schema,前后端同构”。 -
唯一性约束
CouchDB 无全局二级索引,需用 _id 或自定义视图 模拟唯一键。国内做法:把业务唯一键(如手机号)拼成_id = "user::13800138000",利用_id天然唯一性;若业务键可改,则额外建 “锁文档” 机制,通过 MVCC 乐观锁 防止并发重复。 -
冲突验证
多主复制后,同一 doc 会出现 winning rev + 多个 losing rev。验证阶段必须:- 在 validate_doc_update 里拒绝“非法冲突解决”(如库存 < 0)。
- 在 业务层定时巡检 扫描
_conflicts字段,对未自动合并的冲突发企业微信告警。 - 对金融场景,采用 “冲突冻结” 策略:出现冲突即把账户状态置为“待人工审核”,人工复核后再写 resolved_rev。
-
国内合规
等保 2.0 要求 数据完整性 与 操作可追溯。CouchDB 的 _changes feed 可对接 ELK 或 国密 SM3 摘要,实现“每次写入带摘要、每次读取可验签”,面试时提到这一点可体现 合规意识。
答案
“验证数据”在 CouchDB 里是分层的:
- 入口层:用 validate_doc_update 函数在写入前做 schema、业务规则、权限三重校验;性能敏感场景下沉到 Erlang pre-commit hook。
- 唯一性层:利用 _id 天然唯一 或 锁文档+MVCC 解决业务唯一键冲突。
- 冲突层:复制后通过 _conflicts 字段 主动扫描,结合 “冲突冻结” 与 人工复核 保证最终一致性;并在 validate 函数里拒绝非法冲突版本。
- 合规层:把 _changes 序列 同步到 国密 SM3 摘要链,实现等保要求的 可审计、可验签。
上线后,再配合 巡检脚本 + 企业微信告警,形成 “写前拦截、写后审计、冲突冻结、人工兜底” 的完整闭环,才能在国内金融、电商、政务云环境里安心落地。
拓展思考
如果面试官继续追问“离线客户端先写了脏数据,同步时污染全库怎么办”,可回答:
- 客户端同样内嵌 PouchDB + JSON Schema,离线时先本地校验,不合法直接阻断。
- 同步阶段使用 filtered replication,只让通过 design doc 校验函数 的文档上行;脏数据被挡在 CouchDB 服务外,并记录 _replication_state=denied,客户端收到回调后自动回滚本地脏数据。
- 对高敏业务(如医保结算),采用 “双写确认”:客户端写 待审核库,运维人员通过 管理端 二次校验后,用 server-side replication 转到 正式库,实现 “离线可用,上线可控”。