Databricks AUTO CDC 增强版实现双轴历史追踪让审计存续于 VACUUM 之后;Apache Iceberg v3 引入 Variant 半结构化类型;DuckDB 创始人 Hannes Muhleisen 反思行业误诊 SQL 十余年
本期聚焦三条 DataInfra 动态:Databricks 将 AUTO CDC 提升到下一阶段,引入 Bitemporal AUTO CDC(业务时间+系统时间双轴历史追踪),用 STORED AS BITEMPORAL 声明式解决 SEC Rule 17a-4/FINRA 审计合规与乱序事件重写历史难题,并将 Partial Updates 转正 GA、把 AUTO CDC Type 1 Python API 贡献给开源 Apache Spark 4.2;Apache Iceberg v3 spec 新增 Variant 半结构化类型,采用 Apache Parquet 的 VARIANT(1) 编码统一存储异构 JSON 式文档,保留 date/timestamp/decimal 等原生类型;DuckDB 创始人 Hannes Muhleisen 在邮件访谈中反思行业十余年前'误诊'了 SQL——开发者厌恶的从来不是语言,而是安装、服务器与客户端协议,并透露一个未宣布的 DuckDB 新发布预计今年推出。
本期追踪到三条 DataInfra 动态,从声明式变更捕获、半结构化数据存储到单机分析哲学的重新审视,共同指向一个正在发酵的信号:数据基础设施正在从”为长尾集群而建”转向”为真实的工作负载形态而建”——而后者远比我们想象得更简单、更异构。2026 年 8 月 11 日,Databricks 发布 AUTO CDC 增强版,引入 Bitemporal AUTO CDC(业务时间 + 系统时间的双轴历史追踪),用一句
STORED AS BITEMPORAL声明替代数百行脆弱的 MERGE 逻辑,同时解决 SEC Rule 17a-4/FINRA 的审计合规与乱序事件重写历史的难题——更重要的是,它将历史存为数据行而非文件版本,让可复现性真正存活于 VACUUM 之后。Apache Iceberg 则在 v3 spec 中正式纳入 Variant 半结构化类型,采用 Apache Parquet 的VARIANT(1)编码,让异构 JSON 式文档在保留 date/timestamp/decimal 原生类型的前提下共存于同一列,终结了”JSON 当字符串存”与”宽表扁平化”之间的两难。而 DuckDB 创始人 Hannes Muhleisen 在一场邮件访谈中抛出一个尖锐的行业反思:十余年前业界”误诊”了 SQL——2013 年开发者真正厌恶的不是语言本身,而是安装、服务器与客户端协议,Snowflake 与 Amazon 公开的工作负载轨迹显示真正需要集群的分析查询只占极小比例,而数据库行业却花了二十年为这条长尾而建。三条动态分别触及声明式可复现性、半结构化存储、单机分析哲学三个维度,恰好构成了一幅 DataInfra 正在”回归简单、回归真实负载”的完整侧写。
Databricks AUTO CDC 增强版:双轴历史追踪让审计合规与可复现性”存活于 VACUUM 之后”
事件概述
2026 年 8 月 11 日,Databricks 发布博客《Taking AUTO CDC to the next level: Solving the hardest real-world use cases》,作者为 Josh Seidel、Shanelle Roman 与 Sudhanva Huruli。这是对早前”Stop hand-coding change data capture pipelines”一文的延续——AUTO CDC 已在 Apache Spark Declarative Pipelines(SDP)中通过声明式方式自动化了 SCD Type 1、SCD Type 2 与 Snapshot CDC,用几句声明替代了数百行手写 MERGE 逻辑。此次升级直面三类标准 CDC 模式难以解决的现实挑战:乱序的双时态时间线、部分记录更新(NULL 会误覆盖现有数据)、以及”存续于存储保留窗口之外的审计能力”,并将相关能力扩展到开源 Apache Spark 4.2。
技术解析
-
Bitemporal AUTO CDC 双轴历史追踪:标准 SCD Type 2 只能回答”事实何时在真实世界发生变化”,却无法回答”你的系统在某一时点相信什么”。Bitemporal 模式独立追踪两条时间线——业务时间(business time)(事实在真实世界为真的时刻,如股票代码周一生效)与系统时间(system time)(系统何时得知该数据,周一的变更可能周三才进入管道)。每张目标表自动获得四个系统管理列:
__START_AT、__END_AT(业务时间)与__SYSTEM_START_AT、__SYSTEM_END_AT(系统时间)。一个逻辑事实可对应多条物理行(每个业务版本 × 系统版本组合),使沿任一轴的时点重建成为可能。 -
乱序事件的历史重写:核心行为保证是”事件可以以任意顺序在任一时间线上到达”。当一条带更早业务时间或系统时间的更正记录到达时,引擎会重写受影响的历史区间而非简单追加到末尾。SQL 语法为
STORED AS BITEMPORAL+SEQUENCE BY business_time+SYSTEM SEQUENCE BY system_time(注意不是SCD TYPE BITEMPORAL)。排序列必须是可排序类型且不含 NULL 排序值;该功能目前处于 Beta,需将管道固定到channel: PREVIEW,运行在 serverless SDP 或 Pro/Advanced 版本。 -
超越 Time Travel:可复现性存活于 VACUUM 之后:模型训练的可复现性需要数月后仍能重建训练所用数据集。Delta Lake Time Travel 依赖表的文件历史,一旦
VACUUM越过默认 7 天保留窗口删除了旧文件,训练时记录的TIMESTAMP AS OF就会悄悄失效。而双时态表把历史存为数据行而非文件版本——VACUUM与OPTIMIZE只压缩文件、永不触碰逻辑历史,因此每个过去的业务/系统版本仍是可查询的行。做法是把两个 as-of 时刻(业务时间 + 系统时间)作为 MLflow 参数记录,训练查询即可在日后精确重建”当时系统所相信的状态”。 -
Partial Updates 转正 GA:许多 CDC 源只发送变更字段、其余字段填 NULL。Partial Updates 允许更新事件只修改列子集,对选中列而言,入站更新中的 NULL 被解释为”不更新”而非覆盖。以目标行
(1, 'A', 20)与入站(1, NULL, 30)为例,默认 AUTO CDC 会把行改成(1, NULL, 30),开启 Partial Updates 后name的 NULL 被忽略,结果为(1, 'A', 30)。启用只需在 AUTO CDC 定义中加一个参数,三种写法:IGNORE NULL UPDATES ON columnList、IGNORE NULL UPDATES ON * EXCEPT (columnList)、以及逐行不同的源列名COLUMNS TO UPDATE。 -
开源贡献路径:Databricks 将 AUTO CDC Type 1 的 Python API 按 Spark 的常规演进方式(评审提案 + PR,而非一次性代码投放)贡献给 Apache Spark 4.2(见 SPIP 与 SPARK-56249)。乱序数据的正确性内建:一个辅助小表跟踪来自早到事件的删除墓碑状态,重试的微批会收敛而非破坏目标表;因其构建在 Spark 的流式与表抽象之上而非存储格式,可同时运行在 Delta Lake 与 Apache Iceberg 上。SQL 接口
CREATE FLOW ... AS AUTO CDC INTO已合入 master,将在下一版 Apache Spark 发布。
行业影响
这条动态的深层意义在于:CDC 与审计合规正在从”手写胶水代码”变成”声明式基础设施能力”。SEC 自 2021 年以来的记录保存执法行动已在 100+ 家公司身上罚出超 20 亿美元,而”数月后回答参考数据在报告日说了什么、系统当时信了什么”这类问题,恰恰是传统 SCD Type 2 与基于文件版本的 Time Travel 都无法稳健回答的。双时态历史以”数据行”形态存续,从根本上绕开了 VACUUM 带来的可复现性陷阱,把合规与 ML 可复现性的契约简化为 MLflow 里的一对时间戳。对数据工程团队而言,这意味着不再需要为 SEC Rule 17a-4 / FINRA 场景自研一套脆弱的位时态框架。
相关链接
- Databricks 官方博客:https://www.databricks.com/blog/taking-auto-cdc-next-level-solving-hardest-real-world-use-cases
- Bitemporal AUTO CDC 文档:https://docs.databricks.com/aws/en/ldp/cdc#how-bitemporal-auto-cdc-works
- Partial Updates 指南:https://docs.databricks.com/aws/en/ldp/cdc-advanced#apply-partial-updates
- 开源 AUTO CDC 编程指南:https://spark.apache.org/docs/latest/declarative-pipelines-programming-guide.html
Apache Iceberg v3 引入 Variant 半结构化类型:异构 JSON 文档与原生类型在同一列共存
事件概述
Apache Iceberg 官方博客发布《Semi-Structured Data in Apache Iceberg: Meet the Variant Type》,宣布 Iceberg v3 spec 新增 Variant 类型,专为”字段逐行不同的 JSON 式半结构化数据”设计。这是 Variant 系列文章的第一篇,覆盖 Variant 是什么、为何存在、如何融入 Iceberg 表;下一篇将介绍 shredding(将高频访问字段转存为类型化列的技术)。Variant 存储于 Parquet、Avro、ORC 三种格式,而 shredding 目前仅支持 Parquet。
技术解析
-
Variant 解决的问题:传统处理半结构化数据有两条路,各有缺陷——JSON 当字符串存(灵活,但读单个字段要解析整段文本,且 JSON 类型系统薄弱:timestamp 只是字符串、数字精度含糊)与刚性扁平化 schema(查询快,但每个新字段都是一次 schema 迁移,稀疏字段浪费空间)。Variant 兼具 JSON 的灵活性与紧凑的类型化二进制存储:值保留原生类型(date/timestamp/timestamptz/binary/decimal 不再坍缩成 JSON 的字符串与数字),字段名收进字典按 id 引用、无需反复写出全名,且无需预先声明 schema——不同形状的文档共存于同一列,新字段零迁移。
-
在 Iceberg 类型系统中的位置:Variant 在 v3 spec 中自成一类——“既非 primitive 也非 nested”,比 primitive 更丰富,却又不像 struct/list 那样有固定声明形状。它支持嵌套:Variant array(有序、元素类型不受单一类型约束)与 Variant object(字符串键的字段集合、值同为 Variant、字段集不固定)。
-
存储编码(复用 Parquet 而非自定义):Iceberg 没有自定义二进制编码,而是直接采用 Apache Parquet 项目的 Variant 编码,因此一个
variant列映射为 Parquet 的group,含两个 binary 字段:metadata(字段名字典,value里的名字用整数 id 引用)与value(编码后的标量/数组/对象;数组与对象存储每个元素的field_offset字节偏移,对象额外存field_id指向 metadata 字典)。Variant 列本身按字段 ID 寻址(与其他 Iceberg 列一致),但其metadata/value子字段按名访问——这一点对 shredding 至关重要。 -
跨引擎读写:Apache Spark 4.0/4.1 可创建含
VARIANT列的表并读写(读取在 Iceberg 1.10.0 落地于 Spark 4.0;Iceberg 1.11.0 增加 Spark 4.1 及 shredded Variant 写入);Apache Flink 2.1 在 Iceberg 1.11.0 增加 Variant 支持(仅 unshredded,shredded 写入已合入、预计后续版本发布)。因各引擎共用同一 Iceberg Variant 类型,一个引擎写入的值在另一引擎读回保持一致。 -
查询方式:Variant 列要求表为 format version 3(
TBLPROPERTIES('format-version'='3')),写入用parse_json(...),读取用variant_get(column, path, type)提取路径字段,缺字段处返回 null。适合事件/点击流、结构化异构日志、IoT 传感器遥测、第三方 API/webhook 响应(schema 归别人所有且随时可变)、以及本会变成”全 NULL 宽表”的稀疏属性。Variant 不是已知 schema 的替代品——稳定且常查的字段应保持普通类型列,常见模式是”稳定字段独立列 + 可变部分塞进单个 Variant 列”。
行业影响
Iceberg Variant 的意义在于为 Lakehouse 补齐了”半结构化数据”这块长期由 JSON 字符串和宽表两种劣质方案拼凑的拼图。其关键设计选择——复用 Parquet 的 Variant 编码而非自创格式——保证了 Spark、Flink 等引擎在开放表格式内的读写一致性,也与 Databricks Delta 的 Variant 思路形成事实上的编码对齐。而 shredding(下一步)才是真正的性能兑现点:把高频字段转成类型化 Parquet 列,让查询只读所需列、并恢复 per-field min/max 统计做数据剪枝,弥补”整份文档塞进单个 value 列”导致的统计剪枝缺失。对数据平台而言,这意味着”先落异构事件、再按访问模式逐步 shredding”成为一条比”先设计刚性 schema”更务实的演进路径。
相关链接
- Iceberg 官方博客:https://iceberg.apache.org/blog/variant-in-apache-iceberg/
- Iceberg Table Spec(Semi-structured Types):https://iceberg.apache.org/spec/#semi-structured-types
- Apache Parquet Variant 编码:https://github.com/apache/parquet-format/blob/master/VariantEncoding.md
- Variant 跟踪 issue:https://github.com/apache/iceberg/issues/10392
DuckDB 创始人 Hannes Muhleisen 反思:行业十余年前”误诊”了 SQL,真正需要集群的查询只占极小比例
事件概述
在 Lets Data Science 近期发布的一封邮件访谈中,DuckDB 创始人、DuckDB Labs CEO Hannes Muhleisen 抛出一个尖锐的行业反思:十年前业界”误诊”了 SQL。他的核心论点是——2013 年开发者真正厌恶的从来不是 SQL 语言本身,而是随之而来的安装、服务器与客户端协议;那些替换掉 SQL 的 NoSQL 系统,最终又长回了”SQL 形状”的东西,却少了四十年的工程经验积累。访谈中他还透露,一个尚未宣布的 DuckDB 新发布预计今年推出。
技术解析
-
对”NoSQL 取代 SQL”叙事的重估:Muhleisen 指出,把 2013 年开发者对 SQL 的抱怨归因于”语言不好用”是误诊。真正让开发者痛苦的,是数据库的运维负担——安装、起服务器、维护客户端协议。当 NoSQL 系统绕开这套负担迅速流行后,为了表达查询,它们又慢慢长回了类似 SQL 的东西,只是缺少了关系数据库四十年的成熟经验。
-
“集群需求被严重高估”的数据依据:他援引 Snowflake 与 Amazon 公开的工作负载轨迹,称其中只有极小比例的分析查询真正需要集群(“a fraction of a percent”)。而数据库行业在过去二十年里,恰恰是围绕这条”长尾”——需要横向扩展集群的极少数极端场景——来构建的,导致单机本可轻松消化的绝大多数分析负载,被过度工程化了。
-
浏览器内分析应运行”同一引擎”而非玩具版本:Muhleisen 主张,在浏览器内做分析时,用户应运行与服务器端相同的 DuckDB 引擎(得益于 WASM 移植),而不是一个功能缩水的”玩具版”。这与 DuckDB 一贯的”进程内分析数据库”理念一脉相承,也呼应了此前 DuckDB v2.0 规划中异步 I/O、Quack 协议等方向。
-
未宣布的新发布预告:访谈确认了一个尚未公开的 DuckDB 发布预期今年推出(“an unannounced DuckDB launch expected this year”),但未披露具体细节。
行业影响
这条动态的价值不在某个具体版本号,而在于它提供了一个与”云原生、大规模集群”主流叙事相对照的认知锚点。Muhleisen 的论点(与近年 DuckDB、MotherDuck、乃至 Databricks 收购 Electric 后把 Postgres 推到 Agent 沙箱边缘端的动向相互印证)共同指向一个趋势:数据基础设施正在重新发现”单机 + 进程内”这一被集群叙事长期遮蔽的形态。对数据团队而言,这是一个务实的提醒——在默认上马分布式集群之前,先用公开的工作负载数据问一句”我的查询究竟需不需要集群”,可能省下大量不必要的运维与成本。而 DuckDB 今年未宣布的新发布,则值得作为后续追踪的悬念。
相关链接
- Lets Data Science 新闻页(访谈报道):https://www.letsdatascience.com/news
- DuckDB 官方博客:https://duckdb.org/news/
编者评
本期三条动态看似分属三个不同层面——Databricks 的声明式 CDC、Iceberg 的半结构化类型、DuckDB 的单机分析哲学——但把它们并置阅读,能看出一个清晰的共同底色:DataInfra 正在从”为极端场景而过度设计”回归”为真实负载形态而设计”。Databricks 用一句 STORED AS BITEMPORAL 把过去需要自研位时态框架的合规难题变成声明式能力,Iceberg 用复用 Parquet 编码的 Variant 终结了 JSON 字符串与宽表的二选一,而 Muhleisen 则用 Snowflake/Amazon 的公开轨迹数据提醒我们:集群是长尾,不是常态。三者的共同逻辑是降低”正确做事”的门槛——合规不必手写 MERGE、异构数据不必预设 schema、单机分析不必强上集群。值得留意的是,Databricks AUTO CDC 的开源路径(Python API 先入 Spark 4.2、SQL 接口已合 master)与 Iceberg Variant 的 shredding 后续,都还在演进中;而 DuckDB 今年那个”未宣布的新发布”,将是接下来几期值得持续追踪的一条暗线。跨厂商的观察视角依旧是本期想强调的:开放表格式(Iceberg/Delta)在 Variant 编码上的事实对齐、以及”进程内/单机”形态的回归,都不是某一家厂商的孤立动作,而是整个数据基础设施在 Agentic AI 与成本压力双重驱动下的结构性转向。