3、关于spark broadcast

1、spark运行机制

spark是在driver端将由一系列的算子形成的DAG封装成一个个的Task的形式发送到executor中执行的方式运行的,每一个task都是以序列化之后通过网络的形式发送。

2、为什么需要广播?

实际应用场景中会出现需要在task中包含一个很大的变量obj 100M(比如从mysql中查询到的用户信息),那么每次发送task的时候都会把这个obj封装起来一起发送过去,非常的占用网络带宽,而且task体积大占用executor更多的内存。

比如:有一个任务T1, 20个分区,每个分区10个task,一个task里面包含一个100M的数据,则需要占用20G的网络开销。

那么如何减少网络开销?减少task的体积? ==> 广播

3、如何广播,原理是?

Spark有两种广播方式:一种是HttpBroadcast(Spark2.1.0已经移除),另一种是TorrentBroadcast

HttpBroadcast:当执行broadcast.getValue()时,executor没有这个值是,都会通过http网络从driver请求获取,这样会导致driver端压力很大

TorrentBroadcast:在HttpBroadcast的基础上改造成先从“其他executor查找”,如果没有在请求driver。

  • 3.1在driver端执行Broadcast<String> broadcast = jsc.broadcast("大变量");此时driver会将这个值“序列化”,然后得到一个唯一的id,最后生成这个broadcast返回,这个broadcast里面没有“大变量”这个值,只有一个id。所以广播变量一定需要实现序列化接口

    Broadcast对象的内部变量

  • 3.2、在算子中使用broadcast,这样broadcast就会被封装在task中一起封装序列化发送到executor中,当执行到broadcast.getValue()时,会根据这个id首先去executor找,如果没有再通过网络的方式请求driver(一定有),最后缓存到executor的blockManager中。

    由此可以发现,每一个executor中只需要请求一次,后续在他上面运行的task都不需要再次请求driver消耗网络资源。比如现在3个executor,那么网络开销就变成了300M,而且这个大变量是保存在executor中经常要用到的,不需要频繁gc。

4、代码演示==> 在算子执行broadcast.getValue()才有效

````
package com.bigdata.study;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.VoidFunction;
import org.apache.spark.broadcast.Broadcast;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * spark broadcast 测试
 *
 * 几点总结:
 * 1、broadcasts的值必须可以序列化
 *
 * 2、要在算子中使用broadcast.getValue(),而不是在main方法中getValue的到值之后再被算子使用,这样是多此一举
 *
 * 3、算子中的外部变量也必须是可以序列化的,否则封装为task时,报task序列化报错
 *    也就是说,即使不适用广播,直接在main方法里面创建一个不可序列化的对象(比如:mysql 的conn)
 *    然后在算子中使用这个conn,也是不可以的
 *
 * 4、String、int、long、map等基本数据类型都已经实现了Serializable,所以可以直接广播或者在算子中直接使用
 *    mysql的conn、kafka producer、 jedis、hbase client等都没有实现序列化,所以不能广播,只能在算子中创建连接来使用
 *
 * 5、一般来说,广播的信息是:不可变的、较大的(几十M以上)数据。比如说从mysql里面查出来的一些匹配信息
 *
 * @Author liufu
 * @E-mail 1151224929@qq.com
 * @CreateTime 2018/11/19  11:11
 */
public class BroacostTest {
    public static void main(String[] args) throws InterruptedException {
        SparkConf conf = new SparkConf();
        conf.setMaster("local[2]");
        conf.setAppName("BroacostTest");
        JavaSparkContext jsc = new JavaSparkContext(conf);

        // 创建spark RDD
        JavaRDD<String> rdd = jsc.textFile("e:/data.txt");

        /**
         * 广播一个变量TreeMap
         * driver会把treeMap序列化之后得到一个唯一id,然后生成一个Broadcast对象:treeMapBroadcast
         * treeMapBroadcast里面并没有包含数据:"很大的张三",只是有一个id
         * 只有执行treeMapBroadcast.getValue()时,才会通过这个id去executor里面找,如果找不到,再请求driver(一定有),然后缓存到executor
         */
        HashMap<String, String> brMap = new HashMap<>();
        brMap.put("name", "很大的张三");
        Broadcast<Map<String, String>> mapBroadcast = jsc.broadcast(brMap);

        //广播使用方式一:在算子中使用treeMapBroadcast.getValue() ==> 正确,因为算子在executor中执行
        rdd.foreachPartition(new VoidFunction<Iterator<String>>() {
            @Override
            public void call(Iterator<String> iterator) throws Exception {
                /**
                 * 这个算子会封装成task发送到executor执行,而不会在这个main方法中执行
                 * 当执行到treeMapBroadcast.getValue()时,会先请求executor的blockManager
                 * 如果executor没有,则会请求driver的blockManager(一定有)
                 */
                Map<String, String> value = mapBroadcast.getValue();
                System.out.println(value.get("name"));
            }
        });

        //广播使用方式二:在driver取出,然后在算子中使用 ==> 错误,在driver取出来之后,又是一个大变量的具体值,会和算子一起封装在task中发送到executor执行,多此一举
        Map<String, String> value = mapBroadcast.getValue();

        rdd.foreachPartition(new VoidFunction<Iterator<String>>() {
            @Override
            public void call(Iterator<String> iterator) throws Exception {
                System.out.println(value.get("name"));
            }
        });
    }
}
````

转载于:https://my.oschina.net/liufukin/blog/795543

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值