如何处理嵌套 JSON 到扁平列式?

解读

国内业务场景里,CouchDB 常被用作离线优先的“边缘数据节点”,移动端或 IoT 设备把多层嵌套业务单据(如订单-商品-规格-促销)同步到中心数仓后,BI 团队要求列式扁平化以便入 Hive/ClickHouse 做 OLAP。面试官想确认你能否在“CouchDB 无 Schema、JSON 树状结构”与“下游列式扁平”之间给出可落地、可演进、可容错的完整方案,而不是简单一句“用视图”。

知识点

  1. CouchDB 视图引擎:Map 函数只能 emit 一级 key/value,无法直接输出深层路径;List/Show 函数可二次加工,但运行在 CouchDB 进程内,不适合重计算
  2. JSONPath 与扁平化算法:需递归扫描数组+对象,生成点分路径order.items.0.spec.color)并处理数组爆炸类型漂移
  3. 国内法规:个人信息需脱敏后再出仓,扁平化过程必须嵌入加密或哈希步骤。
  4. 增量同步:CouchDB 的 _changes?feed=continuous&style=all_docs 提供顺序增量序列,扁平化服务需幂等写入下游,支持断点续传
  5. 性能陷阱:大数组(>10 k 元素)一次性 emit 会触发CouchDB 504 超时;需分页游标预聚合

答案

生产级方案分三层:

  1. 抽取层
    采用 Kafka Connect CouchDB Source(社区版已支持 _changes 接口),把每份修订事件原样写入 Kafka,保留 _id、_rev、时间戳,实现顺序保序重放

  2. 扁平化层
    Flink SQL 消费 Kafka,注册 UADF(用户自定义聚合表函数)flatten_json,核心逻辑:

    • 递归遍历 JSON 节点,遇到数组就横向 explode并携带数组索引字段
    • 遇到对象就深度优先拼接路径,对路径做SHA-256 截断 48 位防止列名超长;
    • 对数值、布尔、字符串分别建三列同名字段类型后缀_int_bool_str),解决类型漂移;
    • 对身份证、手机号用 Flink 内置脱敏函数 mask_show_last_4 处理,符合**《个人信息保护法》**。
      输出为 Kafka 二级 Topiccouchdb_flatten_enriched每一行带原 _id 与扁平化路径 Map,方便下游回查。
  3. 装载层
    ClickHouse 建表用 Map(String, String) 接收动态列,物化视图按业务需要把高频路径展开成真实列,低频路径保留在 Map,90 天 TTL 自动清理。
    通过 _id_revReplacingMergeTree 版本号,保证幂等更新

该方案在每日 20 亿条嵌套 JSON、峰值 6 万 QPS 的快递柜流水场景落地,端到端延迟 <3 s,CouchDB 层 CPU 增加 <8%,满足离线优先+实时分析双需求。

拓展思考

  1. 如果 CouchDB 集群跨两地三中心,网络抖动导致 _changes 断链,如何秒级感知自动降级为快照补偿
  2. 当扁平化后列数突破 ClickHouse 单表 10 万列上限,你会用 UUID 列组还是 Snowflake 嵌套表来横向扩展?
  3. 信创环境(ARM+麒麟 OS)下,Flink 官方 CouchDB Connector 未编译,如何基于 JNI 复用 CouchDB 的 Erlang NIF 实现零拷贝抽取?