【spark】spark广播变量空指针异常(spark的闭包)

emmmm,大家好,我是菜鸡!

今天来说一个很诡异的问题,广播变量空指针!

起因:

有一个需求:行为表中有1000W人的行为(表名:bt_behavior),但是我只需要特定的500W人的行为,所以直接将行为表和特定的500W人的id关联(表名:dim_user)就可以了,sql大概如下~

#行为表:bt_behavior
#500W人的表:dim_user

select a.* from bt_behavior a
inner join dim_user b 
on a.user_id=b.user_id

这是很简单的一个需求,但是因为数据量特别大,导致了spark程序十分不稳定(隔三差五会跑挂掉),因此想尝试使用其他手段来完成这个需求。首当其冲的我的想到广播变量~

 

正文:

其实之前我就有使用过广播变量来完成一些优化,甚至实现了一些用sql实现不了的功能,这里就不多加赘述。

这个需求就是将dim_user加载成一个广播变量,然后再遍历bt_behavior的每一条数据,判断该条数据的user_id是否存在于dim_user这个广播变量中,存在就保留这条数据,不存在就过滤掉~

 

代码实现:(这个是正确的代码实现)

 public void function() {
    Dataset btBehavior = spark.table("bt_behavior");
    Dataset dimUser = spark.table("bt_behavior")
		.select("user_id");

	//将dimUser的user_id转成list
    List dimUserList = dimUser.map(new MapFunction() {
        public String call(Row value) throws Exception {
            return value.getString(0);
        }
    }, Encoders.STRING()).collectAsList();
	//初始化一个hashMap来做为广播变量的类型,并且给个初始化大小500W
    HashMap map = new HashMap(5000000);
    for (String user : dimUserList) {
        map.put(user,1);
    }
	
	//创建广播变量
    ClassTag> tag = (ClassTag) scala.reflect.ClassTag$.MODULE$.apply(HashMap.class);
    final Broadcast> broadcastMap =  spark.sparkContext().broadcast(map,tag);
	
	
    Dataset originLog = btBehavior.mapPartitions(new MapPartitionsFunction() {
        @Override
        public Iterator call(Iterator input) throws Exception {
            LinkedList btBehaviorDomainList = new LinkedList();

            while (input.hasNext()) {
                Row row = input.next();
                String user_id = row.getAs("user_id");
                //如果该条数据的user_id存在于广播变量中,那就把字段取出
                if (broadcastMap.getValue().containsKey(user_id) ) {
                    String 其他字段1 = row.getAs("其他字段1");
                    String 其他字段2 = row.getAs("其他字段2");
					//...
                    String 其他字段11 = row.getAs("其他字段11");
                    String 其他字段12 = row.getAs("其他字段12");
                 
                    btBehaviorDomainList.add(new btBehaviorDomain(其他字段1,其他字段2,...其他字段11,其他字段12));
                }
            }
            return btBehaviorDomainList.iterator();
        }
    },Encoders.bean(btBehaviorDomain.class));
	
    //保存数据      
    saveData(originLog);
	//清楚广播变量
    broadcastMap.unpersist();
   
}

===================华丽的分割线======================

遇到的异常:

那么我就要来说说我在写这段代码的时候遇到的问题

大家看 :

if (broadcastMap.getValue().containsKey(user_id) )

最开始我不是这么写的,我的写法是:

 if (broadcastMap.getValue().get(user_id)==1 ) 

看起来好像也没什么问题,从map中获取key,判断value是否等于1,如果等于1,证明数据存在,不等于1证明不存在

但是就是这样的写法害惨了我,它报了空指针的异常。

然后我就开始百度,大概百度到了如下的这些文章:

https://bbs.csdn.net/topics/392088491?list=lz

https://www.oschina.net/question/1996639_2234863?sort=default

https://stackoverflow.com/questions/38044082/spark-broadcast-variables-can-not-put-into-a-function-otherwise-threw-nullpoint?r=SearchResults

https://stackoverflow.com/questions/28875921/apache-spark-nullpointer-exception-on-broadcast-variables-yarn-cluster-mode

https://community.cloudera.com/t5/Advanced-Analytics-Apache-Spark/Nullpointer-Exception-on-broadcast-variables-YARN-Cluster/m-p/25314#M597

【spark】spark广播变量空指针异常(spark的闭包)_第1张图片

【spark】spark广播变量空指针异常(spark的闭包)_第2张图片

我发现有人和我遇到了一样离奇的问题,这当中的有些回答让我开始觉得是闭包的问题,因此我觉得是代码或者是spark的BUG

然后我开始研究spark的闭包:http://cwiki.apachecn.org/pages/viewpage.action?pageId=2885948

接着我尝试这样的写法:

 class  SelfMapPartitionsFunction implements MapPartitionsFunction{
        private Broadcast> broadcastMap;
        public SelfMapPartitionsFunction (Broadcast> broadcastMap){
            this.broadcastMap = broadcastMap;
        }
        @Override
        public Iterator call(Iterator input) throws Exception {
            //数据处理....
            return null;
        }
    }


//调用
btBehavior.mapPartitions(new SelfMapPartitionsFunction (broadcastMap));

通过外部传参的方式,将广播变量的引用传入到闭包中。经过测试,SelfMapPartitionsFunction类中确实可以拿到广播变量,但是还是会报空指针的错!!

原因:

后来,我终于发现了原因,HashMap在get数据的时候,如果key不存在,就会报空指针(java.lang.NullPointException),所以并不是广播变量的问题,而且HashMap在查数据我使用错API的问题,真正判断key是否存在应该使用containsKey!!!

 

注意:

最后我想提三点需要注意的问题:

1、如果你是真的遇到闭包的问题,外部的变量在闭包(内部类)中获取不到,可以尝试我如上的方法,将外部变量通过构造方法或者参数传入,来尝试在闭包中获得。

2、使用LinkedList,而不使用ArrayList,因为LinkedList插入数据会比ArrayList快!

3、广播变量使用HashMap,而不使用list,是因为list的判断是否存在是一个一个迭代遍历的,而HashMap是有优化的,在数据量大的情况下,性能上会好非常非常多!

 

所以这个问题,我调试了整整2天的时间,这个故事告诉我们,基础不扎实,就很容易翻车,还是要夯实基础啊!!!

嗯,我是菜鸡下次再见拜拜~

 

你可能感兴趣的:(spark)