基于本文回答

播面 播面

文图音视,全方位拆解八股文
0
评论

找出在同一个订单(或购物车)中经常被同时购买的商品对(Product_A, Product_B)的频次,按组合频次降序排列

面试题:电商购物车/订单商品关联分析

题目描述:
在推荐系统和购物篮分析(Market Basket Analysis)中,找出经常被一起购买的商品组合是非常经典的场景。现在有一张订单商品明细表 order_items,请编写 SparkSQL 查询,找出在同一个订单中经常被同时购买的商品对(Product_A, Product_B)的频次,并按组合频次降序排列。如果频次相同,则按商品 A 的名称升序、商品 B 的名称升序排列。

注意:

  1. 避免重复组合:如 (苹果, 香蕉) 和 (香蕉, 苹果) 视为同一种组合,只保留一种(通常排序靠前的在 A,靠后的在 B)。
  2. 避免自身组合:商品不能和自己组成一对(如 苹果, 苹果)。

数据准备

1. 输入表:order_items(订单明细表)

order_id (订单ID) product_id (商品ID) product_name (商品名称)
1001 P01 苹果 (Apple)
1001 P02 香蕉 (Banana)
1001 P03 樱桃 (Cherry)
1002 P01 苹果 (Apple)
1002 P02 香蕉 (Banana)
1003 P01 苹果 (Apple)
1003 P03 樱桃 (Cherry)
1004 P02 香蕉 (Banana)
1004 P04 榴莲 (Durian)

期望输出

product_a product_b frequency (购买频次)
苹果 (Apple) 香蕉 (Banana) 2
苹果 (Apple) 樱桃 (Cherry) 2
香蕉 (Banana) 榴莲 (Durian) 1
香蕉 (Banana) 樱桃 (Cherry) 1

SparkSQL 参考答案

sql
SELECT 
    a.product_name AS product_a,
    b.product_name AS product_b,
    COUNT(1) AS frequency
FROM order_items a
JOIN order_items b 
  ON a.order_id = b.order_id 
 AND a.product_id < b.product_id
GROUP BY 
    a.product_name, 
    b.product_name
ORDER BY 
    frequency DESC, 
    product_a ASC, 
    product_b ASC;

SparkSQL 深度解析与面试官加分项

在实际的 Spark 岗位面试中,仅仅写出上述 SQL 是不够的。面试官往往会针对这个 SQL 提出一系列深入的性能优化问题。以下是针对该题目的 Spark 核心考点与优化分析:

1. 核心考点:自关联(Self-Join)与去重技巧

  • 非等值连接(Non-Equi Join)去重:
    通过 a.product_id < b.product_id 作为连接条件,一举解决了两个问题:
    1. 避免了商品自我组合(排除了 a.product_id = b.product_id)。
    2. 避免了重复的排列组合(排除了 (P02, P01),只保留 (P01, P02)),从而将排列(Permutation)问题简化为了组合(Combination)问题,数据量直接减半。

2. Spark 性能隐患:数据膨胀(Data Explosion)

  • 痛点:
    自关联会产生 O(N2)O(N^2) 的数据膨胀。如果某个热点订单(例如:大促期间的批发商订单)包含 NN 个商品,自关联后该订单会产生 N×(N1)/2N \times (N-1) / 2 条数据。如果 N=1000N = 1000,一个订单就会膨胀出近 50 万条数据,极易导致 OOM(内存溢出)Executor 运行缓慢
  • 面试加分方案(主动向面试官提及):
    • 限制单订单商品数: 在 Join 之前,过滤掉订单中商品数异常庞大的订单(如 COUNT(product_id) > 50 的订单,这通常是爬虫或异常商家)。
    • 高频商品过滤(剪枝): 实际业务中,可以先统计单品词频,过滤掉极低频的商品,只对高频商品进行关联分析。

3. Spark Shuffle 优化与倾斜(Data Skew)

  • 倾斜原因:
    当使用 GROUP BY a.product_name, b.product_name 时,Spark 会根据这两个字段进行 Hash Shuffle。如果某对商品(如“苹果+香蕉”)是爆款,该 Key 对应的数据会被分发到同一个 Reduce Task 中,导致该 Task 处理时间过长,出现数据倾斜
  • 优化对策:
    • 两阶段聚合(加盐局部聚合):
      先给 Group By 的 Key 加上随机前缀进行局部聚合,再去掉前缀进行全局聚合,分散单点压力。
    • 广播连接(Broadcast Join):
      如果 order_items 表本身经过过滤后尺寸极小(例如限制了只分析某天的数据),可以考虑将其中一份数据广播出去,将 SortMergeJoin 转化为 BroadcastHashJoin,彻底避免 Shuffle。

4. 架构思维扩展:Spark MLlib 替代方案

  • 面试官可能会问: “如果数据量达到数十亿,SQL 自关联完全跑不动,你有什么其他思路?”
  • 标准回答:
    在海量数据下,利用 SQL 进行双表关联求解频繁项集效率极低。推荐使用 Spark MLlib 库中封装好的 FP-Growth(频繁模式增长)算法PrefixSpan 算法。这些算法采用树形结构(FP-Tree)存储数据,只需扫描两次数据库,无需显式进行 O(N2)O(N^2) 的自关联,是大数据场景下处理购物篮分析的标准解决方案。
00:00
00:00