如何存储数据字典?

解读

在国内的 CouchDB 面试中,面试官问“如何存储数据字典”并不是想听“把字典转成 JSON 塞进去”这么简单,而是考察候选人能否把业务语义、版本管理、离线同步、权限隔离、热更新这五个核心痛点一次性闭环。数据字典(下拉选项、码表、枚举、配置项)虽然体量小,却是交易单据、审批流、报表统计的“锚点”,一旦不同节点出现版本分叉,会直接造成业务数据漂移,这是金融、政务、零售等行业验收的红线。因此,回答必须体现“离线优先 + 多主复制”场景下的确定性方案

知识点

  1. CouchDB 文档模型:JSON 文档自带 _id_rev,天然支持多版本合并,适合字典“键-值-描述-状态”四维结构。
  2. 设计文档(_design):可编程存储 + 视图过滤,能把字典当“配置服务”用,避免额外接口。
  3. 复制过滤器_selectorfilter 函数,可让移动端只拉取“本业务线 + 已启用”的字典,节省 70% 流量。
  4. 本地优先策略:PouchDB 离线缓存字典,_rev 作为版本令牌,应用层用 revpos 做增量 diff,实现秒级热更新
  5. 权限隔离:CouchDB 的 _security 对象支持数据库级 ACL,结合 validate_doc_update 可做到“运营人员可改,业务系统只读”,符合国内等保要求。
  6. 灰度发布:利用 _replicator 数据库的 doc_ids 数组,先复制 5% 节点验证,再全量推送,降低生产事故。

答案

我采用“一库一类一分支”的三层模型:

  1. 独立建库 dict_v1_id 规则固定为 type:code:version,例如 gender:1:20240518,保证全局唯一且可排序。
  2. 每条字典文档除 valuelabel 外,显式写入 scopestatuseffectiveFromeffectiveTo,方便视图做时间窗过滤
  3. 在设计文档 _design/dict 中预置两个视图:
    • byType:以 type 为 key,返回最新 _rev,用于移动端增量比对;
    • active:以 [type, status, effectiveFrom] 为复合 key,只输出 status="A" && now() between effectiveFrom-To 的记录,屏蔽未生效或已失效项
  4. 复制阶段,移动端带 filter=app/dict_active,只同步 200KB 以内的热数据;后台运营库通过 validate_doc_update 拒绝非白名单用户修改,确保字典源唯一可信
  5. 版本升级时,不删除旧文档,而是插入新版本并原子切换 status,利用 _rev 冲突机制自动合并,零停机完成热更新
  6. 最后在 _replicator 数据库写入灰度任务,先复制到 dict_v1_gray 验证业务正确性,再全量推送,符合国内金融变更评审流程

拓展思考

如果字典体量膨胀到 10 万条以上,或者需要跨租户隔离,可把“一库一类”升级为“一租户一设计文档”:

  • 利用 CouchDB 3.x 的分片功能,把字典库按 tenantId 做哈希分片,避免单节点热点;
  • validate_doc_update 里加入 tenantIduserCtx.roles 的匹配逻辑,实现行级多租户
  • 对超大体量字典,启用 text 索引(CouchDB 内置 Lucene 插件),把 label 字段做全文检索,毫秒级返回模糊匹配结果
  • 若业务要求秒级广播,可监听 _changes?feed=eventsource,用 Server-Sent Events 推送到前端,替代轮询,在政企内网场景下比 WebSocket 更易过防火墙。