Pinot 1.6.0 RC0 把建表收进 SQL DDL 并扩出三档 IVF 向量后端;Hudi 1.1.2 用 21 个提交清理 Flink 流写的失败边界;DuckDB 让 JSON 文本列不落文件直接变类型化列;v2.0-dev 在 NVIDIA Vera 上跑完 TPC-H SF1000
本期聚焦四条把「原本要靠外部工序兜住的事」收进系统内部的动作:表管理从 API 调用进 SQL DDL,流写的失败与重试从人工补偿进 checkpoint 语义,JSON 文本列从文件中转进一次 SQL 转换,新硬件的红利从不改一行代码的 arm64 二进制里直接拿到。
本期四条动态落在 10 月 16 日至 17 日这个周末窗口,来自三个开源项目的四条独立动作,形状却相当一致:把一件原本要在系统外面绕一道的事,收进系统内部。Pinot 1.6.0 RC0 让建表、删表与物化视图管理有了 SQL DDL 入口,向量后端从 HNSW 一种扩到四选一;Hudi 1.1.2 把 Flink 流写在全局故障转移、内存耗尽、任务重试这些边界上的模糊行为,逐条改成有名字、有提交号的显式处理;DuckDB 用一条 SQL 链路替代了「把 JSON 列 COPY 成文件再 read_json 读回来」的常见绕路;同一天它又把 v2.0 预览版放到 NVIDIA Vera 上跑完整 TPC-H SF1000,取到的
46%优势不需要为这块新芯片改一行代码。四处动作都不是新增一个能力那么简单,而是把一段原本要人介入的工序删除。
Pinot 1.6.0 RC0:建表进了 SQL,代价是 Java 25 与一批旧集成被摘掉
事件概述
Apache Pinot 在 10 月 17 日打出 release-1.6.0-rc0 标签,候选提交为 54f145f79a083abfb816c4c32e6636fc7acc03f6。这是 1.5.1 之后的第一个次版本候选,发布说明的主体结构分两截:前半是特性与可靠性改进,后半是一份密度极高的升级与移除清单。
特性侧最显眼的是 SQL DDL。此前在 Pinot 里管表只能走 controller 的 REST 接口或外部配置,1.6.0 把 CREATE TABLE、DROP TABLE、SHOW TABLES、SHOW CREATE TABLE 开在 controller 的 /sql/ddl 端点上,物化视图一并拿到 CREATE / SHOW / DROP 三类 DDL,并获得单阶段查询改写支持。
与特性清单同样值得读的是后半截。这份候选要求服务侧用 Java 25 或更高版本构建与运行,默认 Scala 二进制版本切到 2.13,原生 text/FST 索引与 TEXT_CONTAINS 被移除,Spark 2 与 Presto 集成被移除,controller 开始拒绝新提交的 TimeFieldSpec schema。对一个已经在生产上的实时 OLAP 集群,这一节的每一条都对应一次迁移。
技术解析
- SQL DDL 与物化视图:controller 暴露
/sql/ddl端点,覆盖CREATE TABLE/DROP TABLE/SHOW TABLES/SHOW CREATE TABLE;物化视图拿到CREATE/SHOW/DROPDDL 并支持单阶段查询改写(#18241、#18544、#18529)。 - 分析能力补齐:
GROUPING SETS、ROLLUP、CUBE在两个查询引擎上都可用;多阶段执行新增流式 distinct 执行与 lookup join 规划改进(#18662、#18817、#19419、#18158)。 - 向量后端从一种扩到四种:在 HNSW 之外新增
IVF_FLAT、IVF_PQ、IVF_ON_DISK三个后端,并带上过滤感知搜索、半径查询与后端调参开关(#17994、#18090、#18119)。三档 IVF 的差别就在内存与精度的取舍上:IVF_PQ走量化压内存,IVF_ON_DISK把开销让给磁盘。 - 数据形态:
OPEN_STRUCT用来承载会演进的嵌套数据,另加逻辑UUID类型、Arrow IPC 批量摄入与 BSON 输入支持(#18368、#19040、#18869、#17871、#18950)。 - 可靠性:改进 null 感知过滤、失败或取消时清理多阶段缓冲区、Kafka offset 处理改为事务性(#19440、#19462、#19161)。
- Java 25 成为门槛:Pinot 服务构建与运行要求 Java 25 或更新版本;Java/JDBC 客户端与显式固定的 SPI/common 构件仍以 Java 11 字节码为目标(#19014)。
- Scala 默认切到 2.13:官方 Spark 3 构件(含
pinot-spark-3-connector)按 Scala 2.13 编译,Maven 构件 ID 不再带 Scala 版本后缀,并要求 Java 25 与按 Scala 2.13 构建的 Spark;要留在 Scala 2.12 得用scala-2.12profile 自行源码构建(#18100)。 - 被移除的旧路径:原生 text/FST 索引与
TEXT_CONTAINS移除,需迁到 Lucene text/FST 设施与TEXT_MATCH(#17998);Spark 2 与 Presto 集成、已弃用的 Kafka high-level consumer 以及 1.3.0 之前弃用的若干 API 一并移除(#17950、#17947、#18931)。 - schema 与配置的两处静默变更:经 controller 提交的新 schema 与被更新的 schema 开始拒绝已弃用的
TimeFieldSpec(fieldType=TIME),已存储的 schema 仍可加载,改用DateTimeFieldSpec(fieldType=DATE_TIME)(#18502);controller 周期性任务的旧别名被移除,controller.retention.frequencyInSeconds要换成controller.retention.frequencyPeriod加一个时长值如6h(提交 8dec968)。 - Kafka 起点默认值改了:Kafka 的原生
auto.offset.reset属性在省略时默认改为earliest。这个属性控制的是 offset 不可用时的恢复行为,与 Pinot 自己的stream.kafka.consumer.prop.auto.offset.reset初始分区 offset 设置是两回事(#19359)。 - 查询语义不再兜着:单阶段查询不再静默忽略
HAVING,不支持的QUALIFY子句被直接拒绝(#19554)。 - RC0 的验证口径:源码在 Git 之外通过
84个模块的 clean install(跳过测试);托管运行环境报30,211个单元测试与1,772个集成测试零失败零错误(跳过32/3个),并做了 Java 11 客户端验证、quickstart 与滚动升级降级检查;最终签名二进制包在两个查询引擎上跑通,COUNT(*)为97,889,无查询异常与部分结果。官方同时说明这些托管运行早于 RC0 的发布、许可与构建打包元数据变更,运行时与测试源码、依赖声明与 RC0 一致。
行业影响
实时 OLAP 引擎这几年的竞争焦点从「能不能实时」转到了「管起来像不像一个数据库」。Pinot 这次把建表收进 SQL DDL,等于承认了前面那套 REST + 配置文件的管理方式在规模化之后是负担:表越多,靠脚本和外部状态维护 schema 的成本越高,出错面也越大。把这条路径挪进 SQL,让表定义能被版本化、能被 diff、能被 CI 检查,这与近两周 Databricks 把多语句事务做成 GA、Looker 把 Agent 指令写进 LookML 走 Git 与 CI/CD 是同一股方向。
代价同样清楚。Java 25 门槛与 Scala 2.13 默认值的组合,会让一批还在 Java 11/17 上的自研服务插件在升级当天就直接失败;原生 text/FST 索引与 TEXT_CONTAINS 的移除,则要求依赖它的查询在升级前重写语义而不只是改函数名。对一个 RC0 来说,这份清单的价值在于把迁移成本提前摊开,而不是等到 GA 之后由使用者自己撞。
相关链接
- Apache Pinot 1.6.0 RC0 发布说明
- SQL DDL(#18241)
- 物化视图 DDL(#18544)
- 单阶段查询改写(#18529)
- GROUPING SETS / ROLLUP / CUBE(#18662)
- 多阶段流式 distinct(#19419)
- IVF 向量后端(#17994)
- 向量后端调参与半径查询(#18119)
- OPEN_STRUCT(#18368)
- Arrow IPC 批量摄入(#18869)
- Java 25 要求(#19014)
- Scala 2.13 默认值(#18100)
- 移除原生 text/FST 索引(#17998)
- 拒绝 TimeFieldSpec(#18502)
- 周期性任务别名移除(8dec968)
- Kafka auto.offset.reset 默认值(#19359)
- HAVING 不再被忽略(#19554)
Hudi 1.1.2:21 个功能性提交,把 Flink 流写的失败与重试边界逐条写清
事件概述
Apache Hudi 在 10 月 17 日发布 release-1.1.2。这个版本号本身需要一点解释:上一个 1.1.x 补丁 release-1.1.1 的发布时间是 2025 年 12 月 18 日,而项目主线已经走到 2026 年 9 月 24 日的 release-1.2.1。换言之,1.1.2 不是主线推进,而是一次把近十个月里已经合入 master 的修复反向移植回旧分支的维护发布——面向那些还没法整体升级到 1.2 线的生产集群。
发布说明正文只有一行 compare 链接,真正的清单藏在 release-1.1.1...release-1.1.2 的 27 个提交里。剥掉版本脚本与 CI 相关的提交后,剩下 21 个带 PR 号的功能性改动,其中 14 个直接挂在 flink 前缀下。这个分布本身就是本次发布的信息:Hudi 1.1 线当前最重要的战场不在新特性,而在 Flink 流写路径的正确性与资源边界。
技术解析
- 丢数据的那条被堵上:全局故障转移时流写丢数据(#19237,2026-07-10 合入)。这是清单里后果最重的一条——流写作业在 failover 后静默丢数据,比作业直接失败更难发现。
- 任务重试时补发元数据:任务重试时重新发送已恢复的写元数据(#20323,2026-10-15 合入),是本轮离发布时间最近的一个提交。
- checkpoint 与 writer 的状态对齐:恢复后 writer 与 RLI 缓存改用已恢复的 checkpoint ID(#19952);coordinator 的
start()失败不再被吞掉,改为重新抛出(#19432)。 - 内存耗尽时的两条防线:不再复用已经分叉的缓冲区(#19692),并抢占非活跃写桶以腾出空间(#19728)。前者是正确性,后者是压力下不雪崩。
- coordinator 的 RPC 与元数据:instant time 请求改为异步下发,避免协调 RPC 超时(#19960);已有 instant 的查找不再被 instant 创建阻塞(#20031);schema 与表属性每个 instant 只校验一次(#20122);coordinator 的 meta client 改为原地刷新(#20134)。
- CDC 与增量读的语义:开启 CDC 时用阻塞式 instant 生成(#18206);pre-8 MOR 增量读改用安全的 log-reader 高水位(#19949)。
- timeline 的几条坑:归档文件过大导致的 timeline 压缩卡死(#17784);存储格式侧忽略临时 LSM timeline manifest(#19659);
LAYOUT_VERSION_1timeline 在CompletionTimeQueryView里用 instant time 作为完成时间查询(#19338)。 - 性能侧:Flink append-only 写路径去掉不必要的 timeline 加载(#18264);lookup 表提交未变化时避免重复重载 timeline(#18930);压缩规划的并行度改为有界(#20024)。
- 写失败的行为被显式化:
hoodie.write.ignore.failed在写数据失败时允许提前失败;该选项在 Hudi 写配置里默认true以保持向后兼容,在 Flink 流摄入里默认false;顺带修掉 coordinator 的提交逻辑,去掉回滚,使 instant 无论写错误如何都会被提交(#12150)。
行业影响
湖仓格式项目的 1.x 维护线过去常被当成一个静态的兼容层,1.1.2 说明这条线其实在持续吸收主线里最硬的那批修复。对生产侧,这批提交的共同点值得注意:它们修的不是「功能不工作」,是「功能在压力下以错误的方式继续工作」。全局 failover 丢数据、内存耗尽时复用分叉缓冲区、coordinator 启动失败被吞、单阶段查询静默忽略 HAVING(Pinot 那边同样是这个形状)——这类问题在日常负载下测不出来,只在故障转移与资源饱和时暴露,一旦爆发就要靠人工对账兜底。
把它们逐条写成有提交号、有选项默认值的显式行为,代价是原来那种「看起来跑通了」的宽松状态不再存在:hoodie.write.ignore.failed 在 Flink 侧默认 false 之后,此前被忽略掉的写错误会浮上来变成作业失败。这是本季度反复出现的同一种交换——用显式的失败换掉隐式的静默。
相关链接
- Apache Hudi 1.1.2 发布页
- 1.1.1 到 1.1.2 的完整提交对比
- 全局故障转移时防止流写丢数据(#19237)
- 任务重试时补发写元数据(#20323)
- 使用已恢复的 checkpoint ID(#19952)
- 重抛 coordinator start() 失败(#19432)
- 内存耗尽时防止复用分叉缓冲区(#19692)
- 内存耗尽时抢占非活跃写桶(#19728)
- instant time 请求异步化(#19960)
- instant 查找不被创建阻塞(#20031)
- 每个 instant 只校验一次 schema(#20122)
- 开启 CDC 时用阻塞式 instant 生成(#18206)
- pre-8 MOR 增量读的安全高水位(#19949)
- 归档文件过大导致 timeline 压缩卡死(#17784)
- 压缩规划并行度有界(#20024)
- 写失败时允许提前失败(#12150)
DuckDB:JSON 文本列不必再落文件,一条 SQL 链路直接转成类型化列
事件概述
DuckDB 团队在 10 月 16 日发布《From JSON Strings to Typed Columns》。文章处理的场景很具体:read_json 能把 JSON 文件读成有类型的表,但现实里大量 JSON 并不以文件形式到达——它是 Postgres 表里的一列事件载荷、是摄入任务存下来的 API 响应、是 CSV 导出里的一个 metadata 字段。社区里反复出现的绕路做法是 COPY 这一列到文件,再用 read_json 读回来。
文章给出的结论是这条往返不需要。从推断结构、修补类型到落成列,全程可以在 SQL 里完成,落盘的文件一步都不出现。这条链路一共四个函数:json_group_structure 推断、json_merge_patch 改类型、json_transform(别名 from_json)转换、unnest 展开。
技术解析
- 先取少量字段的轻路径:
->返回JSON、->>返回VARCHAR,两者都接受键名或 JSON path,缺失键返回NULL。这条路的毛病是每提取一个字段就重新解析一遍 JSON,字段一多就既慢又冗长。 - 推断整列的结构:
json_structure返回单个 JSON 值的结构,它的聚合变体json_group_structure把整列各值的结构合并成一个。输入要求是JSON,所以VARCHAR列要先 cast。示例输出里,出现在任意一行里的键都会进来,包括只在第二行出现的duration_ms与第三行的items数组,整数被推成UBIGINT、ts因为 JSON 没有时间戳类型而推成VARCHAR。 - 大表上用采样换成本:在大表上不必扫全量,
USING SAMPLE 10_000 ROWS便宜得多,代价是极稀有的键会被漏掉。这是一个明确声明的取舍,不是隐藏行为。 - 用
json_merge_patch改类型:结构本身就是一份 JSON 文档,所以可以直接对它打补丁,只列出想改的部分——没列到的user.country、action保留推断结果。补丁结果存进变量payload_structure,后续用getvariable('payload_structure')取用。 - 转换与展开:
json_transform(即from_json)按给定结构把 JSON 值转成 DuckDBSTRUCT,unnest再把顶层字段展开成列。落到表上之后,user是STRUCT(country VARCHAR, id INTEGER)、items是STRUCT(sku VARCHAR, qty INTEGER)[]、ts已是TIMESTAMP。 - 一次性解析的收益:JSON 只在加载时解析一次。此后这些列就是常规 DuckDB 列,按类型存储与压缩,查询不再触碰 JSON。这条性质决定了它不是语法糖——列式的压缩与谓词下推从这一步才开始生效。
- 嵌套展开与命名冲突:
unnest(..., recursive := true)一次展平嵌套结构,默认按最内层键命名,于是user里的id会和事件自身的id撞成两个同名列,后续查询无法区分。传keep_parent_names := true让每层列名带上父级键前缀,user.id与user.country就不再冲突。
行业影响
这篇是教程体量,指向的问题却一直是数据工程里的顽疾:半结构化数据进了仓之后长期停在「文本列」状态,下游每次消费都要重新解析、重新 cast、重新处理缺失键,同一份解析逻辑在十几处 SQL 里各写一遍,且彼此不一致。把「推断—修补—落列」三步收进四个 SQL 函数,等于把这件事从应用侧或预处理脚本里挪回了 SQL 内部,schema 的演化也有了可以被 review 的中间产物(那份结构 JSON)。
放在更大的背景里看,这与 Iceberg v3 的 Variant 类型、Doris 4.1.5 里对 VARIANT 子路径规划的修复是同一条战线:嵌套与半结构化数据正在从「能存下来」走向「能像定长列一样被优化」。DuckDB 这条路的取舍也清楚——它给的是一次性的转换而非持续演进的 schema,结构变了要重新跑一遍;USING SAMPLE 的漏键风险由使用者承担。这是一次把控制权交给使用者的设计,不是自动化的。
相关链接
- From JSON Strings to Typed Columns(2026-10-16)
- DuckDB 工程博客索引
- JSON 函数文档
- JSON 类型文档
- unnest 文档
- SAMPLE 语法文档
- SET VARIABLE 文档
DuckDB v2.0-dev 在 NVIDIA Vera 上跑完 TPC-H SF1000:46% 的优势不靠改代码
事件概述
同一天(10 月 16 日),Gábor Szárnyas 与 NVIDIA 团队合作发布了《Benchmarking DuckDB v2.0-dev on NVIDIA Vera》。测试不是挑几条读查询,而是完整的 TPC-H 工作负载,规模因子 1000。结果以 QphH@Size 综合分计:x86 基线(Intel Xeon 6)三次运行的中位数为 2,109,782.19,NVIDIA Vera 为 3,073,292.99,Vera 领先 46%。
这次测试里最值得记的一点是软件侧的零改动。Vera 跑 arm64 二进制,DuckDB 的常规构建不需要任何修改就能直接运行。硬件换了,代码一行没换。
技术解析
- 硬件规格:NVIDIA Vera 有
88个 Olympus 核,带 Spatial Multithreading,内存带宽最高1.2TB/s,面向 agentic 工作负载;x86 基线是 Intel Xeon 6,96核开超线程得192线程。 - 统一的软件与系统环境:两台机器都跑 Ubuntu 26.04、
768GB 内存、单块3TB 以上 NVMe SSD、ext4 文件系统。写文章时 DuckDB v2.0 尚未发布,用的是预览构建1.6.0.dev379,基准框架为duckdb-tpch,固定在提交cf096cea8a0a054cefe0e695c842ca5bca6ec04d。 - 线程与 NUMA 的处理:x86 侧实测 DuckDB 在
96线程下表现最好,故SET threads=96;Vera 侧是双路系统,测试用numactl --cpunodebind=0 --membind=0限制在单个 socket 上,并固定SET threads=88。这个约束意味着 Vera 的数字是在「只用一半机器」的条件下拿到的。 - 内存配置:针对 SF1000 这种规模且吞吐测试阶段高度并发的负载,设置了
block_allocator_memory='500G'与allocator_background_threads=true。 - 数据生成:用 Rust 重写的并行 TPC-H 数据生成器
tpchgen-cli高效生成大规模数据集,更新流与查询流仍用旧生成器产出。 - 三轮取中位数:先做一次预热并丢弃结果,随后顺序跑三轮,取
QphH@Size的中位数。三轮分别是 x862,120,588.82/ Vera3,073,292.99、x862,022,795.01/ Vera3,131,306.27、x862,109,782.19/ Vera3,052,102.87。 - 优势归因:文章给了三条——高内存带宽带来快速扫描;配合低内存延迟,让核心在处理期间持续被占满;Olympus 核的大缓存使聚合与连接中构建的哈希表能被高效访问。这三条恰好对应扫描、流水线和哈希表三类分析负载的基本操作。
- 结果的外部坐标:文章提到若通过官方 TPC-H 渠道审计,两个平台都有机会进入 SF1000 榜单前三。
行业影响
分析引擎受益于硬件迭代这件事本身不新鲜,值得记的是这次的「零成本」性质:没有移植、没有新后端、没有为这块芯片编译特殊二进制,arm64 上现成的构建直接跑出了 46%。对一个以可嵌入为设计目标、跑在从手机到服务器各种地方的引擎来说,这验证了架构选择的可移植性红利——硬件厂商把核数、带宽、缓存堆上去,软件侧不用跟着改。
对基础设施选型的直接含义是:TPC-H SF1000 这个量级的分析负载,瓶颈已经高度集中在内存带宽与缓存层次,而不是指令集或编译器优化。这也解释了为什么测试要专门限制在单个 socket 上并公开这一约束——在 NUMA 边界内测量,数字才有可比性。作者说明后续会用 DuckDB v2.0.0 的稳定构建、在完整双路配置上、纳入其他基准继续测,也就是说当前的 46% 是一个中间读数。
相关链接
编者评
本期四条动态分属三个项目,指向的是同一件事的不同切面:缩短数据从一种形态变成另一种形态的路径。Pinot 把建表从 REST 调用与配置文件收进 SQL DDL,是让表定义这条路径变短、变可版本化;Hudi 把 Flink 流写在故障与资源边界上的行为逐条写死,是让「出错之后回到一致状态」这条路径从人工对账变成 checkpoint 语义;DuckDB 用四个函数替代文件中转,是让 JSON 文本列变成类型化列这条路径不再出仓;Vera 上的 46% 则是把「新硬件 → 新性能」这条路径上的移植工作删到零。
四条里三条谈的是接口收敛,一条谈的是可移植性。前者的代价高度一致:原先模糊但省事的做法不再可用——Pinot 要换 Java 与 Scala 版本、要重写 TEXT_CONTAINS 查询、要接受 HAVING 不再被静默忽略;Hudi 在 Flink 侧把 hoodie.write.ignore.failed 默认改成 false 之后,此前被吞掉的写错误会变成作业失败。这个季度里这条交换反复出现:宁可要一个显式报错,也不要一个静默通过。后者的意义则在另一侧——当分析负载的瓶颈明确落在内存带宽与缓存层次上时,软件侧的护城河不再是针对某块芯片的调优,而是能不能在架构变化时一行不改地跟上。
需要交代的覆盖情况:本轮扫描窗口落在周末,产出密度明显低于工作日。Databricks 官方 feed 最新一条仍是 10 月 16 日 21:00 的多语句事务 GA(已在 Vol.95 报道),10 月 17—18 日无新条目;AWS Big Data Blog 最新为 10 月 15 日,AWS what’s new 在 10 月 17—18 日无数据基建类条目;Google Cloud 发布说明在 10 月 17—18 日无条目,数据云周更汇总最新版仍是 October 12–16 那一期;Trino 仍停在 484、Doris 仍停在 4.1.5、Iceberg 仍停在 1.12.0、Delta Lake 最新为 08-20、Flink 博客最新为 10-10 CDC 3.7.0、dbt 博客最新为 10-09;Anyscale 最新为 10-15 的 KubeRay 文章,Dremio 首页仍是无日期的常青解释稿。中文源方面,阿里云博客、阿里云开发者社区大数据页与火山引擎文档站在本轮依旧取不到可解析的 10 月 17—18 日条目,这是连续第十五期零可用;本期的国产开源观察由 Pinot 与 Hudi 两条承接,Doris、Paimon、StarRocks、SeaTunnel、Amoro、Kyuubi、Linkis、InLong 在窗口内均无新版本。本期两条 DuckDB 内容日期为 10 月 16 日,此前未被本专栏覆盖,本轮一并补入。