航向追踪
Apache Spark 4.1.0-preview3 发布
来源: https://lists.apache.org/thread/9y3t9j6ntg30os6os1c30dy5b2prqt8y
Spark 4.1.0-preview3相比Spark 4.1.0-preview2的核心变化:
- 环境升级:新增对 Kubernetes v1.34 和 Swift 6.2 的支持,深度适配 Java 21。
- 生态增强:优化 Spark Connect 远程 API,对齐 DataFrame 行为。
- 架构稳固:彻底完成 Log4j 2 迁移,清理传递依赖冲突。
- 分析能力:增强 SQL 结构化日志分析功能。
该版本侧重于兼容性扩展与依赖清理,是向正式版过渡的关键迭代。
前线研讨
关于 Parquet 谓词下推中 IN 转 OR 阈值优化的技术权衡
来源:https://lists.apache.org/thread/o3r1o3mybqt3d102m6nqoyhzo4gdywt2
现象与痛点
- 现象: 当用户在 Spark SQL 中使用 IN 子句(如 id IN (1, 2, 3...))查询 Parquet 文件时,Spark 会根据配置 spark.sql.parquet.pushdown.inFilterThreshold(默认值为 10)决定其去向。
- 痛点: 如果 IN 列表中的值超过 10 个,Spark 就不再将该过滤条件下推给 Parquet。
预期与目标
- 目标: 希望通过增加该配置的阈值(允许更多的值进行下推),让更多复杂的 IN 过滤能够在存储层(Parquet)直接完成。
- 预期收益: 减少磁盘 I/O,利用存储层的跳块(Skipping)能力,提升整体查询响应速度。
各方见解
- Asif Shahid 的技术见解:
- 他支持更宽泛的将 IN 转换为 OR 链条下推。
- 内存优化论: 他认为转换成 OR 链后,执行时变成了简单的线性判断,避免了在每个 Partition 上创建 HashSet(Map lookup)的开销,从而节省内存分配和查找成本。
- Yuming Wang 的警示(通过引用 PR):
- 结构性限制: 提醒大家注意 Parquet Filter2 API 的局限。Parquet 处理 OR 是基于递归二叉树的。
- 性能瓶颈: 当 OR 条件过多(阈值设得太大),会导致生成的谓词树极其深,这不仅会增加 Driver 端生成计划的负担,还可能在 Executor 解析该树时导致 StackOverflowError(栈溢出) 或 CPU 瓶颈。
总结复盘
- 核心权衡(Trade-off): 这是一个 "I/O 节省" vs "CPU/内存稳定性" 的博弈。
- 调大阈值: 适合 IN 列表在几十个到上百个,且数据分布稀疏的场景(此时 I/O 节省远大于 CPU 解析开销)。
- 保持低阈值: 是一种安全策略,防止不可控的超长 IN 列表(如几千个值)拖垮 Driver 或让读取逻辑崩溃。
- 结论
- 默认值 10 是一个极其保守的"安全线"。在实际生产中,如果确定 IN 列表不会达到几千个,通常可以根据机器性能手动调优至 50-100。
- 通过这个问题我们可以思考一下sparksql在执行时,是怎么解析执行的?哪部分代码是在driver执行的,哪部分代码是在executor执行的。
Spark 多阶段聚合重写引发的性能疑问
来源:https://lists.apache.org/thread/jmblqxq4mf67o9p1y2k97h097pw6djxl
现象与痛点
- 现象: 开发者在执行 groupBy("date") 聚合时,发现物理执行计划(Physical Plan)中莫名多出了一个按 (date, user_id) 进行的分组阶段。
- 痛点:
- 资源浪费: 逻辑上只需要按日期汇总,底层却执行了更细粒度的 Shuffle 和聚合。
- 正确性疑虑: 担心 count_distinct 触发的执行计划重写,会导致非累加性函数 percentile(百分位数)计算结果出错。
- 性能损耗: 这种多级的 ObjectHashAggregate 在海量数据下会显著拖慢任务进度。
预期与目标
- 合规性确认: 确认这种"多级聚合"是 Spark 的预期行为还是潜在的 Bug。
- 原理溯源: 弄清楚为什么 count_distinct 会干预 percentile 的物理执行路径。
- 方案优化: 在结果正确的前提下,如何消除冗余计算,提升执行效率。
各方见解
- Herman van Hovell的见解:
- 这是 Spark 处理 count_distinct 的标准逻辑。为了确保去重准确,Spark 会将任务拆分为: 局部聚合,按 (分组键 + 去重键) 物理重组数据;全局聚合,在去重后的基础上计算最终结果。
- 这不是资源浪费,正确性保障: 虽然物理计划发生了重写,但 Spark 的优化器(Catalyst)能够确保 percentile 在多级聚合下的逻辑一致性。
- FengYu Cao的见解:
- 确认了在他们的案例中(user id, date)预去重的情况下,count_distinct执行与否结果没有差异,但是会有额外的性能损耗(产生额外的ObjectHashAggregate)
总结复盘
- 最佳实践优化:
- 如果你在生产环境中有类似的"去重+百分位"需求,建议先检查上游数据的唯一性。如果是为了计算活跃用户数,考虑使用 approx_count_distinct 来进一步释放计算资源。
- 日常开发注意:
- 性能优化时需要多观察物理计划,警惕物理计划中涉及shuffle的操作,不必要的shuffle对性能会产生很大影响。
Spark的JSON解析行为不符合JSON标准(RFC 8259)
来源:https://lists.apache.org/thread/83ywfzb37lpktljz3rzlhgdrqwfkhyzr
现象与痛点
- 跨系统兼容风险: 这种"宽松"的解析机制导致 Spark 能够读取被其他标准工具视为"损坏"的 JSON 数据,造成跨系统兼容性隐患。
- 单引号问题: Spark 默认允许使用单引号定义字符串(allowSingleQuotes=true),但标准 JSON 仅允许双引号,单引号应被视为畸形数据。
- 非转义字符问题: Spark 允许在字符串中直接出现未转义的控制字符(如 \n),而标准要求 U+0000 至 U+001F 的字符必须经过转义。
预期与目标
- 核心诉求:推动 Spark 向 JSON 行业标准靠拢。
- 引入新的配置项来约束解析行为,使其与标准对齐。
- 提升数据的健壮性,对于不符合 RFC 8259 的数据应明确识别为"损坏"或"非法"。
各方见解
- Philo的见解:
- 希望Spark和JSON标准接轨。将单引号视为非法。
总结复盘
- spark社区对此目前没有回应。也就是说近期不会对此进行改进。
- 问题背景在于与Hive的深度绑定: Spark 在发展初期为了快速获取 Hive 用户,其JSON处理逻辑大量参考了Hive的JsonSerDe。Hive作为一个面向 SQL 的工具,在处理字符串时对引号并不敏感,许多旧系统导出的数据本身就充斥着单引号或未转义的换行符。 另外"容错"即美德,在分布式计算中,如果因为千万分之中的一条数据"不合规"(比如多了一个控制字符)就导致整个计算任务(Job)失败,对用户来说是不可接受的。因此,Spark默认选择了Jackson解析器最宽松的PERMISSIVE模式。
方案思辨
指标与语义建模(The metrics & semantic modeling in Spark)
核心动机
目前在 Spark 中定义和使用业务指标存在以下问题:
- 逻辑重复与维护困难:用户通常需要为不同的维度组合(如"按月活跃用户"和"按地区活跃用户")创建多个 SQL 视图,导致逻辑重复和复制粘贴错误 。
- 计算结果不一致:复杂的指标(如比例或去重计数 COUNT DISTINCT)在被重新聚合时,往往会得到错误的结果 。
- 治理混乱:难以确定哪个视图是指标的"官方"版本,且由于语义层往往存在于引擎之外,优化器和权限控制无法有效介入 。
- LLM 集成困难:缺乏统一、确定的资产,使得 AI 代理或大模型难以在不同用例中获取一致且准确的数据 。
关键设计
该提案建议在 Spark 中引入一种名为 "指标视图 (Metric View)" 的一级对象 :
-
核心理念:将指标定义(Measures)与维度划分(Dimensions)解耦。用户只需定义一次业务逻辑,即可按任意维度组合进行查询 。
-
主要组成部分:
- Measures(指标):一种新型列类型,支持在没有预定义 GROUP BY 的情况下进行聚合(如 SUM(revenue)) 。
- Dimensions(维度):作为常规字段,用于对数据进行过滤和切分(如 Country, Date) 。
- 使用方式:用户可以使用熟悉的 SQL 语法进行查询,系统会在查询时动态扩展为正确的聚合表达式并进行查询规划 。
- 示例查询:SELECT MEASURE(revenue), country FROM metric_view GROUP BY ALL 。
影响价值
- 对数据工程师:只需维护单一的指标定义,无需维护大量近似重复的视图,大大降低了维护成本和错误率 。
- 对分析师/科学家:可以灵活地按不同维度拆解数据,且始终能得到一致、正确的答案,消除了"为什么数据对不上"的困扰 。
- 对平台管理:实现了在引擎层面统一的治理、权限控制和"单一事实来源(Single Source of Truth)" 。
- 对下游应用:为 AI 代理、BI 工具和 LLM 提供了可靠、确定性的数据基础,使自动生成的分析报告更加可信 。
社区探讨
- 赞成观点
- 随着目前AI的兴起,Text2SQL存在准确性的问题,AI很难写出复杂聚合逻辑,尤其是非加性指标。如果Spark内部有了明确的MEASURE,AI就不用重头生成复杂聚合SQL,能够降低AI理解数据的难度。
- 如果指标定义在Spark内部,Spark的Catalyst可以更好的预聚合,提高查询性能。
- 反对观点
- MEASURE这一新概念破坏了标准SQL的直观性,现在可以在没有group by时也能聚合,让SQL开发者违背直觉,会让不熟悉该特性的开发者感到困惑。
- MEASURE这一概念应当提到更高级的组件中入hive或其他第三方组件,不应由Spark承担"语义层"的职责。因为如果指标的概念存在第三方元数据管理平台,flink或其他引擎可以复用这一能力。
- 这会使得Catalyst变得过于臃肿,难以维护。
- 针对提案中提到的"多表关联自动去重(Symmetric Aggregation)",自动处理 Join 导致的重复计算(Fan-out)在技术上非常复杂。如果 Spark 自动帮用户做了"去重"或"聚合重写",当结果不符合预期时,用户将极其难以调试。与其在引擎内部通过黑盒逻辑处理,不如让用户显式地通过 SQL 写清楚,或者在数据预处理阶段(ETL)解决掉。
观察随笔
- 笔者持保留意见,如果能被LLM利用,那当然很好。但是就目前来看还比较遥远,而且LLM发展的力度很可能很快就可以写出复杂SQL。另外指标这种业务概念,随着大数据目前湖仓一体的架构背景下,指标应该复用给其他引擎,不应当只限于Spark。
生态拓扑
Apache Spark Connect Swift Client 0.4.0 发布
https://github.com/apache/spark-connect-swift/releases/tag/0.4.0
Spark connect是spark社区在3.4版本开始大力推广的一项能力。Spark Connect 是一种基于 gRPC 的解耦架构,它允许开发者通过轻量级 API 远程连接 Spark 集群,实现"像访问数据库一样"跨语言、低开销地操作大数据。
- 协议升级:全面升级至 gRPC Swift 2,并重新生成了相关代码。
- 功能扩展:新增对 Timestamp 类型的支持,支持 createDataflowGraph、startRun 等算子。
- 环境适配:兼容 Swift 6.2 及 Spark 4.1.0-preview,优化了 macOS 和 Ubuntu 上的集成测试。
Apache Spark Kubernetes Operator 0.5.0 发布
https://github.com/apache/spark-kubernetes-operator/releases/tag/0.5.0
Apache Spark Kubernetes Operator 是一种云原生控制平面,它将 Spark 作业和集群抽象为 Kubernetes 的自定义资源(CRD),从而实现通过标准 K8s 工具链自动化部署、管理及扩展 Spark 应用。
- 环境适配:新增对 Kubernetes v1.31 至 v1.33 的支持。
- Spark版本:支持 Apache Spark 3.5 和 4.0。
- CRD升级:将 SparkApp 和 SparkCluster 的自定义资源定义(CRD)推进至 v1beta1 版本。
- 功能增强:支持为 SparkCluster 配置水平自动伸缩(HPA),并丰富了使用示例。