如何验证数据?

解读

在国内 CouchDB 面试中,面试官问“如何验证数据”并不是想听“写个 if 判断”这种通用答案,而是想确认候选人是否真正落地过 分布式、离线优先、多主复制 场景下的数据质量保障。
核心考点有三层:

  1. 单文档层面:JSON 结构、业务规则、必填字段、值域、格式。
  2. 跨文档层面:关联一致性、业务唯一性、状态机合法性。
  3. 复制与冲突层面:因 CouchDB 多主复制必然产生冲突,验证必须覆盖 冲突检测、冲突解决、最终一致性 全过程。

回答时要体现“离线优先”思维:客户端先写本地,再异步同步,验证逻辑必须 在客户端、服务端、同步链路 三段同时生效,否则上线后会出现“手机端看数据合法,同步后全库污染”的生产事故。

知识点

  1. validate_doc_update
    CouchDB 原生在设计文档里提供 validate_doc_update 函数,用 JavaScript 写,每次写入触发,可抛 forbidden/unauthorized 错误。国内项目常配合 role-based 权限 做“同一条数据,普通用户只能改自己的字段,管理员可改全部字段”的细粒度校验。

  2. Erlang 前置钩子(couch_epi 插件)
    对性能要求高的场景(如秒杀库存),可在 Erlang 层写 pre-commit hook,在文档落盘前做 内存级强校验,比 JavaScript 快一个数量级,国内头部电商的库存中心采用此方案。

  3. JSON Schema 校验
    若团队前端用 Node.js,可直接复用 JSON Schema 文件;CouchDB 自身不内建,但可在 validate_doc_update 里手动调用 ajv 等库,实现“一套 schema,前后端同构”。

  4. 唯一性约束
    CouchDB 无全局二级索引,需用 _id 或自定义视图 模拟唯一键。国内做法:把业务唯一键(如手机号)拼成 _id = "user::13800138000",利用 _id 天然唯一性;若业务键可改,则额外建 “锁文档” 机制,通过 MVCC 乐观锁 防止并发重复。

  5. 冲突验证
    多主复制后,同一 doc 会出现 winning rev + 多个 losing rev。验证阶段必须:

    • validate_doc_update 里拒绝“非法冲突解决”(如库存 < 0)。
    • 业务层定时巡检 扫描 _conflicts 字段,对未自动合并的冲突发企业微信告警。
    • 对金融场景,采用 “冲突冻结” 策略:出现冲突即把账户状态置为“待人工审核”,人工复核后再写 resolved_rev
  6. 国内合规
    等保 2.0 要求 数据完整性操作可追溯。CouchDB 的 _changes feed 可对接 ELK国密 SM3 摘要,实现“每次写入带摘要、每次读取可验签”,面试时提到这一点可体现 合规意识

答案

“验证数据”在 CouchDB 里是分层的:

  1. 入口层:用 validate_doc_update 函数在写入前做 schema、业务规则、权限三重校验;性能敏感场景下沉到 Erlang pre-commit hook
  2. 唯一性层:利用 _id 天然唯一锁文档+MVCC 解决业务唯一键冲突。
  3. 冲突层:复制后通过 _conflicts 字段 主动扫描,结合 “冲突冻结”人工复核 保证最终一致性;并在 validate 函数里拒绝非法冲突版本。
  4. 合规层:把 _changes 序列 同步到 国密 SM3 摘要链,实现等保要求的 可审计、可验签
    上线后,再配合 巡检脚本 + 企业微信告警,形成 “写前拦截、写后审计、冲突冻结、人工兜底” 的完整闭环,才能在国内金融、电商、政务云环境里安心落地。

拓展思考

如果面试官继续追问“离线客户端先写了脏数据,同步时污染全库怎么办”,可回答:

  • 客户端同样内嵌 PouchDB + JSON Schema,离线时先本地校验,不合法直接阻断。
  • 同步阶段使用 filtered replication,只让通过 design doc 校验函数 的文档上行;脏数据被挡在 CouchDB 服务外,并记录 _replication_state=denied,客户端收到回调后自动回滚本地脏数据。
  • 对高敏业务(如医保结算),采用 “双写确认”:客户端写 待审核库,运维人员通过 管理端 二次校验后,用 server-side replication 转到 正式库,实现 “离线可用,上线可控”