最近使用SparkStreaming對公司交互產品的日誌進行處理最後插入Hbase和Redis,數據經Flume收集後入Kafka,然後途徑Sparkstreaming應用,最後插入相應數據庫中;
然後發現在數據產生的高峯期Sparkstreaming居然發生數據計算積壓的情況,也就是任務積壓導致的阻塞,由於公司環境是內網,Spark job界面也無法查看,無法排查是哪裏的問題;只能用linux命令各種排查:
- 首先考慮是服務器資源不足的情況,於是增加資源緊盯內存和cpu看,發現利用率特別低,此項排除
- 然後考慮是磁盤IO,簡單測試了一下,IO速率 253M/s,此項排除
- 正直愁眉不展之際在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();
}
}
});
}
});