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的原因

相关推荐
做个有深度的老李4 分钟前
协作机器人选型方法论|基于企业组织形态与生产模式的选型框架
大数据·机器人·自动化·柔性机器人
ofoxcoding32 分钟前
借助 CLAUDE.md 约束 Sonnet 5.5 多文件重构行为的提示词实践
大数据·elasticsearch·ai·重构
ZHOUPUYU35 分钟前
PHP 高级技术实战:从原理到高并发架构落地
android·性能优化·php
HZZD_HZZD1 小时前
厂区三级计量怎么选表?互感器倍率与需量管理
大数据·物联网·腾讯云
计算机毕业编程指导师1 小时前
大数据毕设选题推荐:基于Hadoop+Spark全球碳排放减排策略分析与可视化系统源码 毕业设计 选题推荐 毕设选题 数据分析 机器学习
大数据·hadoop·python·计算机·spark·毕业设计·碳排放减排
墨香幽梦客2 小时前
SSL/TLS握手过程全拆解:从ClientHello到Finish的关键步骤与性能优化
网络协议·性能优化·ssl
lisw052 小时前
大数据对大数据挖掘及其处理框架带来的挑战
大数据·人工智能·数据挖掘
派小心.2 小时前
多端数据分析平台的漏斗可以跨端搭建吗?5 步搭出跨端转化漏斗
大数据·运维·服务器·前端·数据分析
见闻小天地2 小时前
数据中心柴发出口保护怎么选?Emax 与 Tmax XT 的定位与分工
大数据·运维·人工智能·业界资讯
anxiao_m2 小时前
跨地域大文件怎么传?2026主流传输软件实测对比
大数据·数据库·文件传输