記一次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();
                                }
                            }
                        });
                    }
                });

 

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