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

相关推荐
YangYang9YangYan3 小时前
2026高职大数据与会计专业学数据分析的技术价值分析
大数据·数据挖掘·数据分析
AI智能探索者8 小时前
揭秘大数据领域特征工程的核心要点
大数据·ai
做cv的小昊9 小时前
【TJU】信息检索与分析课程笔记和练习(8)(9)发现系统和全文获取、专利与知识产权基本知识
大数据·笔记·学习·全文检索·信息检索
AC赳赳老秦9 小时前
DeepSeek 私有化部署避坑指南:敏感数据本地化处理与合规性检测详解
大数据·开发语言·数据库·人工智能·自动化·php·deepseek
C7211BA11 小时前
通义灵码和Qoder的差异
大数据·人工智能
三不原则11 小时前
银行 AIOps 实践拆解:金融级故障自愈体系如何搭建
大数据·运维
大厂技术总监下海13 小时前
数据湖加速、实时数仓、统一查询层:Apache Doris 如何成为现代数据架构的“高性能中枢”?
大数据·数据库·算法·apache
新诺韦尔API15 小时前
手机三要素验证不通过的原因?
大数据·智能手机·api
成长之路51416 小时前
【数据集】分地市全社会用电量统计数据(2004-2022年)
大数据
InfiSight智睿视界16 小时前
门店智能体技术如何破解美容美发连锁的“标准执行困境”
大数据·运维·人工智能