记一次Sparkstreaming应用之Hbase插入数据优化

最近使用SparkStreaming对公司交互产品的日志进行处理最后插入Hbase和Redis,数据经Flume收集后入Kafka,然后途径Sparkstreaming应用,最后插入相应数据库中;

然后发现在数据产生的高峰期Sparkstreaming居然发生数据计算积压的情况,也就是任务积压导致的阻塞,由于公司环境是内网,Spark job界面也无法查看,无法排查是哪里的问题;只能用linux命令各种排查:

  1. 首先考虑是服务器资源不足的情况,于是增加资源紧盯内存和cpu看,发现利用率特别低,此项排除
  2. 然后考虑是磁盘IO,简单测试了一下,IO速率 253M/s,此项排除
  3. 正直愁眉不展之际在sparkstreaming的运行日志发现了这么一句

ResultStage 29324 (foreachPartition at NameStreaming.java:418) finished in 5.826 s

看着有点像job任务结束完成的日志,然后对着和程序代码一比较,还真是,感情sparkstreaming会将每个计算完成的任务的完成时间日志都输出来。。。emmmm  之前由于运行日志打印速度特别快,根本没发现。。。

好了,现在能定位到job对应代码具体操作的位置,发现有个两个插入hbase的job任务完成时间一共花了52s,一个插入redis的job任务话费了17s,而其他操作的job都是毫秒级别,纳尼,我一个实时应用你居然给我跑出一分多钟的速度。。。。那还叫实时?十分生气,研究了一下代码感觉没啥问题啊  百度了一下,首先去优化hbase参数,调整了以下两个参数:

hbase.regionserver.handler.count
export HBASE_HEAPSIZE=4G

结果不断重启调整参数一点卵用也没有!!

又搜到一个看起来比较靠谱的结果,大概内容就是说hbase批量提交能让插入速率提升一个量级,也就是十倍!细想了一下也对,我的程序是每次都进行单条数据插入,高峰期的时候数据量特别大,很拉低速度的!就像你在oracle上面插入数据,每次你插入一条数据你都需要提交一次,但是如果我一次就插入一批数据,然后我就只需要提交一次!修改代码后发现果然job任务完成时间居然控制在了1s内!是不是很强大。然后对应插入redis情况一样,使用redis的pipeline管道批量插入!

还有一个坑点是,由于插hbase代码中涉及到原子操作,

table.incrementColumnValue

最后实在没办法,将该原子操作拆为批量查询和当前入库数据合并计算后再批量插入hbase!验证数据没有问题,搞定收工!需要注意的是这种不适合大规模的数据操作,实在有这种要求就不要使用hbase了!最后插一段redis批量插入的代码:

javaPairDStream1.foreachRDD(new VoidFunction<JavaPairRDD<String, String>>() {
                    @Override
                    public void call(JavaPairRDD<String, String> stringStringJavaPairRDD) throws Exception {
                        stringStringJavaPairRDD.foreachPartition(new VoidFunction<Iterator<Tuple2<String, String>>>() {
                            @Override
                            public void call(Iterator<Tuple2<String, String>> tuple2Iterator) throws Exception {
                                JedisUtil jedisUtil = JedisUtil.getInstance(serverProps);
                                Jedis jedis = jedisUtil.getJedis();
                                Pipeline pipeline = jedis.pipelined();
                                while (tuple2Iterator.hasNext()) {
                                    Tuple2<String, String> tuple2 = tuple2Iterator.next();
                                    String key = tuple2._1();
                                    String value = tuple2._2();
                                    try {
                                        pipeline.sadd(key, value);
                                        //log.info("###################key=" + key + "#########总pv写入redis#######value=" + value + "#######################################");
                                    } catch (Exception e) {
                                        log.error("===========total pv save to redis is error,key is " + tuple2._1() + "====value is " + tuple2._2());
                                    }
                                }
                                try {
                                    pipeline.sync();
                                } catch (Exception e) {
                                    log.error("==================计算总pv插入redis报错!====================");
                                } finally {
                                    pipeline.close();
                                    jedis.close();
                                }
                            }
                        });
                    }
                });

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章