spark性能优化2:Window操作和groupBy操作的区别

GroupBy操作

什么时候触发shuffle

python 复制代码
# 以下操作都会触发shuffle
df.groupBy("column").count()
df.groupBy("column").sum("value")
df.groupBy("column").agg(F.sum("value"), F.avg("value"))

为什么触发shuffle

因为groupBy需要将具有相同key的数据重新分布到同一个分区中进行聚合计算。

Window操作

什么时候不触发shuffle

python 复制代码
pythonfrom pyspark.sql.window import Window

# 如果数据已经按partitionBy的列进行了分区,则不会触发shuffle
window1 = Window.partitionBy("segment_id")
df.withColumn("count", F.count("*").over(window1))

# 即使添加排序也不会触发shuffle(只要数据已经按partitionBy列分区)
window2 = Window.partitionBy("segment_id").orderBy("event_time")
df.withColumn("rank", F.row_number().over(window2))

什么时候会触发shuffle

Window操作本身不会触发shuffle,但如果数据没有按照 partitionB y 的列进行预先分区,则在执行Window操作前可能需要进行shuffle来重新组织数据。

Window操作和GroupBy操作有什么区别

我在使用这两种操作时出现了混淆,下面来看下它们之间的区别

核心区别

  1. Window操作
    Window操作是一种逐行计算的操作,它不会改变数据的行数,只是为每一行添加新的计算列。
  2. GroupBy操作
    GroupBy操作是一种聚合操作,它会将多行数据聚合成一行,减少数据的行数。
    举个具体例子

假设我们有以下数据:

bash 复制代码
segment_id	value
A	10
A	20
B	30
B	40

使用Window操作:

python 复制代码
pythonwindow_spec = Window.partitionBy("segment_id")
df.withColumn("sum_value", F.sum("value").over(window_spec))

结果:

bash 复制代码
segment_id	value	sum_value
A	10	30
A	20	30
B	30	70
B	40	70

使用GroupBy操作:

python 复制代码
pythondf.groupBy("segment_id").sum("value")

结果:

bash 复制代码
segment_id	sum(value)
A	30
B	70

Window版本 (优化后):

python 复制代码
def segment_count_over_100(df):
    """保留分段条数大于100的数据"""
    # 使用Window函数避免shuffle操作
    window_spec = Window.partitionBy("segment_id")
    df_with_count = df.withColumn("segment_count", F.count("*").over(window_spec)) \
                     .filter(F.col("segment_count") > 100) \
                     .drop("segment_count")
    return df_with_count

这个操作:

  1. 为每一行添加一个 segment_ count 列,表示该segment_id的总记录数
  2. 然后过滤掉记录数小于100的行
  3. 最后删除临时列 segment_count
  4. 行数可能会减少,但不是通过聚合,而是通过过滤

GroupBy版本 (原始):

python 复制代码
def segment_count_over_100(df):
    """保留分段条数大于100的数据"""
    # 统计segment的数据的条数,保留大于100条数的数据
    count_df = df.groupBy("segment_id").count().filter(F.col("count") > 100)
    df = df.join(count_df, "segment_id").drop("count")
    return df

这个操作:

  1. 先对数据进行聚合,得到每个segment_id的记录数
  2. 过滤掉记录数小于100的segment_id
  3. 将结果与原始数据进行join操作
  4. 行数可能会减少,通过聚合和连接实现

Shuffle情况分析

  • Window版本:

    • 不触发shuffle(假设数据已经按segment_id分区)
    • 在每个分区内独立计算
  • GroupBy版本:

    • 触发shuffle(需要将相同segment_id的数据发送到同一节点)
    • 然后再触发一次shuffle(join操作需要将数据重新分布)

为什么结果相同但性能不同

虽然两种方法最终得到的数据是相同的,但它们的执行过程完全不同:

  1. Window版本:

    • 保持原有数据行数
    • 通过窗口函数计算每个分组的统计信息
    • 通过过滤操作移除不需要的行
    • 只在必要时触发shuffle
  2. GroupBy版本:

    • 先聚合减少行数
    • 再通过join操作恢复详细数据
    • 多次触发shuffle

使用Window函数的版本更加高效,因为它:

  1. 避免了多次shuffle操作
  2. 保持了数据的局部性
  3. 减少了网络传输开销
  4. 实现了相同的业务逻辑

这就是为什么我们推荐使用Window函数替代GroupBy + Join的原因

相关推荐
数据科学小丫33 分钟前
数据分析与FineBI介绍
大数据·数据分析·finebi
ALex_zry39 分钟前
Git大型仓库推送失败问题完整解决方案
大数据·git·elasticsearch
猿小喵1 小时前
索引优化-MySQL性能优化
数据库·mysql·性能优化
二进制coder2 小时前
Git Fork 开发全流程教程
大数据·git·elasticsearch
better_liang4 小时前
每日Java面试场景题知识点之-线程池配置与优化
java·性能优化·面试题·线程池·并发编程
天硕国产存储技术站6 小时前
DualPLP 双重掉电保护赋能 天硕工业级SSD筑牢关键领域安全存储方案
大数据·人工智能·安全·固态硬盘
雷文成.思泉软件6 小时前
以ERP为核心、企微为门户,实现一体化集成
大数据·低代码·创业创新
东哥说-MES|从入门到精通8 小时前
数字化部分内容 | 十四五年规划和2035年远景目标纲要(新华社正式版)
大数据·人工智能·数字化转型·mes·数字化工厂·2035·十四五规划
南飞测绘视界8 小时前
上市公司绿色专利申请、授权数据(1999-2024年)
大数据·专利·上市公司
一个天蝎座 白勺 程序猿9 小时前
KingbaseES在政务领域的应用实践——武汉人社大数据平台“数字化服务新模式”
大数据·数据库·政务·kingbasees·金仓数据库