You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spark多列去重/聚合:哈希列vs原列方案性能对比

Spark DataFrame多列去重方案对比:哪种更优?

我需要对Spark DataFrame的3列(id_1、id_2、id_3)执行去重操作,现有两种方案,结合以下补充条件,想知道哪种更优:

方案一

df = df.withColumn(
        "hash_dup",
        f.hash(
            f.coalesce(f.col("id_1"), f.lit("")),
            f.coalesce(f.col("id_2"), f.lit("")),
            f.coalesce(f.col("id_3"), f.lit(""))
        )
    ).dropDuplicates(["hash_dup"])

方案二

df = df.dropDuplicates(["id_1","id_2","id_3"])

补充说明

  • LE1:针对TB级高基数键组合数据,Spark会使用SortAggregate而非HashAggregate。
  • LE2:考虑到Hash为Int类型,比多列字符串占用内存更少,想了解数据类型和列数是否会影响性能。
  • LE3:鉴于hash存在碰撞问题,猜测使用sha2进行对比会更优。

结论先行:方案二更优,且不建议用hash/sha2替代原字段去重

1. SortAggregate场景下的性能对比

在TB级高基数、依赖SortAggregate的场景里,方案二直接基于原字段去重的性能反而更好:

  • 方案一多了一步withColumn计算hash列的开销,TB级数据量下,这部分额外计算会被大幅放大,拖慢整体执行速度。
  • SortAggregate的核心是排序+去重,不管是排序原字段还是hash值,时间复杂度都是O(n log n)。但原字段排序时,Spark能利用列存储的优化特性(比如Parquet/ORC的字典编码、列压缩)减少IO开销;而hash列是临时计算生成的,没有这些优化加持,实际IO和内存占用未必比原字段更优——哪怕hash是Int类型,你也得先把原字段读出来计算hash,这一步的内存开销已经存在了。

2. 数据类型与列数的影响

你提到hash是Int类型内存占用更少,但这里存在误区:

  • Spark处理字符串字段时,会通过字符串池复用重复值,即便高基数场景下,原字段的内存管理也经过了优化;而计算hash时,每个字段组合都要生成新的Int值,这部分内存是额外新增的。
  • 多列复合排序时,Spark的SortAggregate对内存的管理是专门优化过的,不会比单列hash排序占用更多内存——反而因为不需要额外存储hash列,整体内存开销更低。

3. hash碰撞与sha2的问题

  • f.hash返回的是Int类型,碰撞概率极高,TB级高基数数据下,几乎一定会出现不同字段组合生成相同hash的情况,直接用hash去重会导致错误去重(误删不同行),这是业务上绝对不能接受的。
  • 换成sha2(比如sha2(concat_ws("|", id_1, id_2, id_3), 256))确实能降低碰撞概率,但sha2生成的是长字符串,内存占用比原字段组合还大,而且计算sha2的开销远高于原字段排序,完全得不偿失。
  • 哪怕要做哈希去重,也必须在hash之后用原字段做二次校验,但这样就失去了hash去重的意义,反而比方案二多了计算hash+二次校验两步操作,性能更差。

总结

对于TB级高基数数据的去重,直接用dropDuplicates指定原字段是最优选择:既保证结果完全正确,又避免了额外计算开销,还能利用Spark对原字段存储的优化特性。


内容的提问来源于stack exchange,提问作者bmcristi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 02:16:02