spark的join操作

本文通过实例详细介绍了Spark中的三种连接操作:join、left-outer-join 和 right-outer-join,并对比了Hive SQL中的相应操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

https://www.cnblogs.com/kangoroo/p/7778962.html




spark三种连接join

本文主要介绍spark join相关操作。

讲述spark连接相关的三个方法join,left-outer-join,right-outer-join,在这之前,我们用hiveSQL先跑出了结果以方便进行对比。

我们以实例来进行说明。我的实现步骤记录如下。

 

1、数据准备

2、HSQL描述

3、Spark描述

 

1、数据准备

我们准备两张Hive表,分别是orders(订单表)和drivers(司机表),通过driver_id字段进行关联。数据如下:

orders

orders表有两个字段,订单id:order_id和司机id:driver_id。司机id将作为连接键。

通过select可以看到三条数据。

复制代码
hive (gulfstream_test)> select * from orders;
OK
orders.order_id orders.driver_id
1000    5000
1001    5001
1002    5002
Time taken: 0.387 seconds, Fetched: 3 row(s)
复制代码

 

drivers

drivers表由两个字段,司机id:driver_id和车辆id:car_id。司机id将作为连接键。

通过select可以看到两条数据。

hive (gulfstream_test)> select * from drivers;
OK
drivers.driver_id       drivers.car_id
5000    100
5003    103
Time taken: 0.036 seconds, Fetched: 2 row(s)

 

 

2、HSQL描述

JOIN

自然连接,输出连接键匹配的记录。

可以看到,通过driver_id匹配的数据只有一条。

hive (gulfstream_test)> select * from orders t1 join drivers t2 on (t1.driver_id = t2.driver_id) ;
OK
t1.order_id     t1.driver_id    t2.driver_id    t2.car_id
1000    5000    5000    100
Time taken: 36.079 seconds, Fetched: 1 row(s)

 

LEFT OUTER JOIN

左外链接,输出连接键匹配的记录,左侧的表无论匹配与否都输出。

可以看到,通过driver_id匹配的数据只有一条,不过所有orders表中的记录都被输出了,drivers中未能匹配的字段被置为空。

复制代码
hive (gulfstream_test)> select * from orders t1 left outer join drivers t2 on (t1.driver_id = t2.driver_id) ;
OK
t1.order_id     t1.driver_id    t2.driver_id    t2.car_id
1000    5000    5000    100
1001    5001    NULL    NULL
1002    5002    NULL    NULL
Time taken: 36.063 seconds, Fetched: 3 row(s)
复制代码

 

RIGHT OUTER JOIN

右外连接,输出连接键匹配的记录,右侧的表无论匹配与否都输出。

可以看到,通过driver_id匹配的数据只有一条,不过所有drivers表中的记录都被输出了,orders中未能匹配的字段被置为空。

hive (gulfstream_test)> select * from orders t1 right outer join drivers t2 on (t1.driver_id = t2.driver_id) ;
OK
t1.order_id     t1.driver_id    t2.driver_id    t2.car_id
1000    5000    5000    100
NULL    NULL    5003    103
Time taken: 30.089 seconds, Fetched: 2 row(s)

 

 

3、Spark描述

spark实现join的方式也是通过RDD的算子,spark同样提供了三个算子join,leftOuterJoin,rightOuterJoin。

在下面给出的例子中,我们通过spark-hive读取了Hive中orders表和drivers表中的数据,这时候数据的表现形式是DataFrame,如果要使用Join操作:

1)首先需要先将DataFrame转化成了JavaRDD。

2)不过,JavaRDD其实是没有join算子的,下面还需要通过mapToPair算子将JavaRDD转换成JavaPairRDD,这样就可以使用Join了。 

下面例子中给出了三种join操作的实现方式,在join之后,通过collect()函数把数据拉到Driver端本地,并通过标准输出打印。

需要指出的是

1)join算子(join,leftOuterJoin,rightOuterJoin)只能通过PairRDD使用;

2)join算子操作的Tuple2<Object1, Object2>类型中,Object1是连接键,我只试过Integer和String,Object2比较灵活,甚至可以是整个Row。

这里我们使用driver_id作为连接键。 所以在输出Tuple2的时候,我们将driver_id放在了前面。

 

Join.java

复制代码
/*
*   spark-submit --queue=root.zhiliangbu_prod_datamonitor spark-join-1.0-SNAPSHOT-jar-with-dependencies.jar
* */
public class Join implements Serializable {

    private transient JavaSparkContext javaSparkContext;
    private transient HiveContext hiveContext;

    /*
    *   初始化Load
    *   创建sparkContext, sqlContext, hiveContext
    * */
    public Join() {
        initSparckContext();
        initHiveContext();
    }

    /*
    *   创建sparkContext
    * */
    private void initSparckContext() {
        String warehouseLocation = System.getProperty("user.dir");
        SparkConf sparkConf = new SparkConf()
                .setAppName("spark-join")
                .set("spark.sql.warehouse.dir", warehouseLocation)
                .setMaster("yarn-client");
        javaSparkContext = new JavaSparkContext(sparkConf);
    }

    /*
    *   创建hiveContext
    *   用于读取Hive中的数据
    * */
    private void initHiveContext() {
        hiveContext = new HiveContext(javaSparkContext);
    }


    public void join() {
        /*
        *   生成rdd1
        * */
        String query1 = "select * from gulfstream_test.orders";
        DataFrame rows1 = hiveContext.sql(query1).select("order_id", "driver_id");
        JavaPairRDD<String, String> rdd1 = rows1.toJavaRDD().mapToPair(new PairFunction<Row, String, String>() {
            @Override
            public Tuple2<String, String> call(Row row) throws Exception {
                String orderId = (String)row.get(0);
                String driverId = (String)row.get(1);
                return new Tuple2<String, String>(driverId, orderId);
            }
        });
        /*
        *   生成rdd2
        * */
        String query2 = "select * from gulfstream_test.drivers";
        DataFrame rows2 = hiveContext.sql(query2).select("driver_id", "car_id");
        JavaPairRDD<String, String> rdd2 = rows2.toJavaRDD().mapToPair(new PairFunction<Row, String, String>() {
            @Override
            public Tuple2<String, String> call(Row row) throws Exception {
                String driverId = (String)row.get(0);
                String carId = (String)row.get(1);
                return new Tuple2<String, String>(driverId, carId);
            }
        });
        /*
        *   join
        * */
        System.out.println(" ****************** join *******************");
        JavaPairRDD<String, Tuple2<String, String>> joinRdd = rdd1.join(rdd2);
        Iterator<Tuple2<String, Tuple2<String, String>>> it1 = joinRdd.collect().iterator();
        while (it1.hasNext()) {
            Tuple2<String, Tuple2<String, String>> item = it1.next();
            System.out.println("driver_id:" + item._1 + ", order_id:" + item._2._1 + ", car_id:" + item._2._2 );
        }

        /*
        *   leftOuterJoin
        * */
        System.out.println(" ****************** leftOuterJoin *******************");
        JavaPairRDD<String, Tuple2<String, Optional<String>>> leftOuterJoinRdd = rdd1.leftOuterJoin(rdd2);
        Iterator<Tuple2<String, Tuple2<String, Optional<String>>>> it2 = leftOuterJoinRdd.collect().iterator();
        while (it2.hasNext()) {
            Tuple2<String, Tuple2<String, Optional<String>>> item = it2.next();
            System.out.println("driver_id:" + item._1 + ", order_id:" + item._2._1 + ", car_id:" + item._2._2 );
        }

        /*
        *   rightOuterJoin
        * */
        System.out.println(" ****************** rightOuterJoin *******************");
        JavaPairRDD<String, Tuple2<Optional<String>, String>> rightOuterJoinRdd = rdd1.rightOuterJoin(rdd2);
        Iterator<Tuple2<String, Tuple2<Optional<String>, String>>> it3 = rightOuterJoinRdd.collect().iterator();
        while (it3.hasNext()) {
            Tuple2<String, Tuple2<Optional<String>, String>> item = it3.next();
            System.out.println("driver_id:" + item._1 + ", order_id:" + item._2._1 + ", car_id:" + item._2._2 );
        }
    }

    public static void main(String[] args) {
        Join sj = new Join();
        sj.join();
    }

}
复制代码

 

执行结果

其中Optional.absent()表示的就是null,可以看到和HSQL是一致的。

复制代码
复制代码
Application ID is application_1508228032068_2746260, trackingURL: http://10.93.21.21:4040
 ****************** join *******************
driver_id:5000, order_id:1000, car_id:100                                       
 ****************** leftOuterJoin *******************
driver_id:5001, order_id:1001, car_id:Optional.absent()
driver_id:5002, order_id:1002, car_id:Optional.absent()
driver_id:5000, order_id:1000, car_id:Optional.of(100)
 ****************** rightOuterJoin *******************
driver_id:5003, order_id:Optional.absent(), car_id:103
driver_id:5000, order_id:Optional.of(1000), car_id:100
复制代码
复制代码

由于数据量不大,我没有从执行效率上进行考量。

根据经验,一般在数据量较大的情况下,HSQL的执行效率会高一些,如果数据量较小,Spark会快。 


<think>好的,用户想比较和理解Spark Join与MapReduce Join的区别及用法。首先,我需要回顾一下这两个框架的基本原理,以及它们在处理数据连接时的不同方法。 首先,MapReduce是Hadoop的经典计算模型,基于分治思想,分为Map和Reduce两个阶段。而Spark是基于内存计算的分布式框架,引入了弹性分布式数据集(RDD)和更丰富的操作符。两者的执行机制不同,这会影响Join操作的实现方式。 接下来,执行流程的区别。MapReduce Join通常需要在Reduce阶段进行数据合并,需要将相同键的数据通过网络传输到同一个Reducer,这可能导致较大的网络开销。而SparkJoin操作可以利用内存计算和更灵活的分区策略,可能减少数据移动,提高效率。 然后,数据分发的机制。MapReduce依赖Shuffle过程,所有相同键的数据必须传输到同一个节点,这在处理大规模数据时可能成为瓶颈。Spark的优化策略如Broadcast Join可以将小表广播到各个节点,避免大规模的Shuffle,这对于大小表连接特别有效。 性能方面,Spark的内存计算和DAG调度器能优化任务执行顺序,减少磁盘I/O,而MapReduce每一步都需要写磁盘,速度较慢。另外,Spark支持多种Join策略(如SortMergeJoin、BroadcastHashJoin),根据数据大小自动选择最优策略,而MapReduce的Join策略相对固定,需要手动优化。 使用场景方面,MapReduce适合超大规模数据且对延迟不敏感的场景,比如离线批处理。而Spark更适合需要低延迟和迭代计算的场景,比如机器学习或实时分析,尤其是在需要多次操作同一数据集时,Spark的内存优势明显。 此外,用户可能还想了解具体的优化方法,比如在Spark中如何选择Join类型,或者如何调整分区策略来优化性能。同时,他们可能对两种框架的API和易用性感兴趣,比如Spark的DataFrame API比MapReduce的Java代码更简洁。 需要检查引用内容是否有相关支持。引用3提到Spark的BroadcastHashJoinExec和如何通过ChildDistribution优化数据分布,这可以解释Spark的广播连接机制。引用4提到高级API如Hive优化执行计划,可能涉及Spark SQL的优化器。这些都可以作为回答的支撑。 总结,用户需要明确的对比点包括执行机制、性能、优化策略、适用场景以及易用性。需要结构清晰,分点说明,并引用相关技术细节来增强可信度。同时,生成的相关问题要引导用户进一步探索优化方法和实际应用案例。</think>### Spark Join与MapReduce Join的对比分析 #### 一、执行机制差异 1. **MapReduce Join** 采用分阶段Shuffle机制,需通过Map阶段输出键值对,Reduce阶段完成数据合并。例如处理表$A$和表$B$的Join时: - 两表数据按Join Key哈希分区 - 所有相同Key的数据通过网络传输到同一Reducer - 实现方式包括**Reduce端Join**(通用但效率低)和**Map端Join**(需小表预加载到内存)[^3] ```python # MapReduce伪代码示例(Reduce端Join) def map(key, value): emit(join_key, (table_tag, value)) def reduce(key, values): for (tag, data) in values: if tag == 'A': a_list.append(data) else: b_list.append(data) for a in a_list: for b in b_list: emit(key, (a,b)) ``` 2. **Spark Join** 基于内存计算和DAG优化,支持多种Join策略: - **Broadcast Hash Join**:小表广播到所有Executor,避免Shuffle(适用于$小表 \bowtie 大表$)[^3] - **Sort Merge Join**:大表间Join时先分区排序再合并(类似MapReduce但内存优化) - 自动选择策略(通过`spark.sql.autoBroadcastJoinThreshold`配置) #### 二、性能关键指标对比 | 维度 | MapReduce Join | Spark Join | |---------------------|---------------------------------|--------------------------------| | **数据移动** | 强制全量Shuffle | 可避免Shuffle(如Broadcast) | | **磁盘I/O** | 每阶段写磁盘 | 内存优先,减少磁盘交互 | | **延迟** | 分钟级~小时级 | 秒级~分钟级 | | **适用数据量** | 超大规模数据(PB级) | 中小规模数据(TB级以下更优) | #### 三、典型应用场景 1. **MapReduce Join适用场景** - 离线批处理(如每日全量用户日志分析) - 数据规模极大且硬件资源有限 - 需与Hadoop生态系统深度集成(如Hive on MapReduce) 2. **Spark Join优势场景** - 迭代计算(如机器学习特征关联) - 实时性要求较高的ETL流水线 - 多数据源混合处理(通过Spark SQL统一操作) - 需要复杂Join条件(如`df1.join(df2, expr("a.id = b.id AND a.ts > b.ts"))`) #### 四、优化实践对比 - **MapReduce优化** - 使用Combiner减少数据传输量 - 手动实现布隆过滤器过滤无效Key[^3] - **Spark优化** - 调整`spark.sql.shuffle.partitions`控制并行度 - 利用`cache()`缓存复用数据集 - 通过AQE(Adaptive Query Execution)动态优化Join策略[^4] #### 五、代码复杂度对比 ```python # Spark实现Join(Python API) df_result = df1.join(df2, "join_key", "inner") # MapReduce实现同等功能需约200行Java代码 ``` Spark通过高阶API将复杂性隐藏在运行时优化中[^4],而MapReduce需手动控制数据流。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值