事件生成JOB调优笔记(spark streaming)
来源:互联网 发布:java解析json数组 编辑:程序博客网 时间:2024/06/05 08:33
说明:
1.Spark Streaming Application资源配置executor-number:3,executor-memory:2g,driver-memory:2g(因为是我们自研平台上运行的APP的Application,所以资源不能申请太多)
2.时间窗口:5min
业务场景:
有三种原始日志,其中一种是告警日志取名为evt(用event_id和device_ip标记为唯一的一条,会不断发送相同的告警,但end_time、severity会变化),另外有两种流量日志取名为evt_srcip_traffic和evt_traffic_total分别表示该告警中关联的源IP的流量和告警流量。事件生成要做的事情很简单:
1.两种流量日志需要归并(比如evt_traffic_total相同event_id和device_ip的同一天的需要合并为一条,流量之类的需要取总值和峰值)
2.需要生成事件和事件详情,可以理解为:
evt表
event_id device_ip stime etime
1 1.1.1.1 2016-08-04 00:00:00 2016-08-04 01:00:00
1 1.1.1.1 2016-08-04 00:00:00 2016-08-04 02:00:00
1 1.1.1.1 2016-08-04 00:00:00 2016-08-04 03:00:00
evt_traffic_total
event_id device_ip bytes packets
1 1.1.1.1 1 1
1 1.1.1.1 2 2
evt_srcip_traffic
event_id device_ip sip bytes packets
1 1.1.1.1 1.1.1.2 1 1
1 1.1.1.1 1.1.1.3 2 2
最后生成的事件是:
event_id device_ip stime etime tbytes tpackets mbytes mpackets
1 1.1.1.11 2016-08-04 00:00:00 2016-08-04 03:00:00 3 3 2 2
生成的事件详情是:
event_id device_ip sip stime etime tbytes tpackets mbytes mpackets
1 1.1.1.1 1.1.1.2 2016-08-04 00:00:00 2016-08-04 03:00:00 3 3 2 2
1 1.1.1.1 1.1.1.3 2016-08-04 00:00:00 2016-08-04 03:00:00 3 3 2 2
两种日流量归并:
total_merge
event_id device_ip stat_time tbytes tpackets mbytes mpackets
1 1.1.1.1 2016-08-04 3 3 2 2
sip_merge
event_id device_ip sip stat_time tbytes tpackets mbytes mpackets
1 1.1.1.1 1.1.1.2 2016-08-04 3 3 2 2
1 1.1.1.1 1.1.1.3 2016-08-04 3 3 2 2
前提:
数据全都是从kafka读取的,16个partition,并行读的好处是快,当然可以理解为接到的顺序是无序的了
业务特征:
1.收到的数据日流量表数据可以直接单表生成,而事件表和事件详情表的数据则需要两种日志进行补齐字段
2.每一批数据都会触发四张表的插入(全新数据)、更新(已有数据的结束时间、流量等字段更新)
难点:
1.如何知道哪些数据需要更新,哪些数据需要插入
2.插入的数据用copy很快,但更新的数据update就算是batch update也非常慢(表上有4个左右索引,为了前台的查询)
tips:pg的更新有点小变态,它的MVCC(多版本控制),所以每update一次(即使是非索引字段)它也会新建一个新行,并把老行标记为删除状态;然后如果新行和老行在一个数据块里,则索引无需更新(HOT更新);如果新行和老行不在一个数据块里,则索引需要更新,造成速度比较慢
已验证存在缺憾的方法:
1.每收到一批数据,首先用它拼接where语句,然后去pg库查询已有的数据,然后取出所有已有的数据和内存中的数据一起在内存里更新,关联不上的(新增的)则在内存里加入,最后delete需要更新的数据,将“插入+更新”的数据copy到表里
结论:a.sql是有长度限制的,数据量大的情况下sql长度超过限制,那么只能改为500条一批的查询,大大影响查询效率 b.所有操作都在driver上进行,分分钟out of memory c.索引多delete也慢,所以用delete+copy取代update+copy一点也没有看到优势
2.先用sqlcontext把pg的表load为dataframe,再用内存的dataframe,两个dataframe都注册为表,用内存的表左关联pg的dataframe的表,如果右表没有字段为null,则表明是需要更新的记录,反之则为需要插入的记录;更新记录作为dataframe1,插入的记录作为datafram2,两个dataframe在mappartitions里做批量的更新和copy
结论:1.sqlcontext直接load全表很慢,它并不能够智能的根据后一步左关联只取一部分数据 2.batch update很慢
优点:所有操作都在executor上进行了,且不会把数据全部屯在内存,不会再出现out of memory了
最优解法:
1.将内存中的dataframe保存为pg中的表,用tmp_开头
示例:
String tbRandomName = Constant.DBNAME + "."+ "tmp_" + java.util.UUID.randomUUID().toString().replace("-", "_");
Properties props = new Properties();
props.put("user", username);
props.put("password", password);
evtTrafficTotalDayMerge.write().jdbc(String.format("jdbc:postgresql://%s:%s/%s", ip, port, database), tbRandomName, props);
2.利用sqlcontext jdbc读取左关联的表,得到需要更新的dataframe和需要插入的dataframe
示例:
Map<String,String> options1 = new HashMap<String,String>();
options1.put("url",String.format("jdbc:postgresql://%s:%s/%s?user=%s&password=%s",ip,port,database,username,password));
options1.put("dbtable","(select xxx from tmp_table left join evt on xxxx.... where evt.xx is not null) as temp");
DataFrame relateUpdateDf = sqlContext.read().format("jdbc").options(options1).load();
3.需要更新的dataframe mapparittions,将它存为临时表;然后使用update xxx set xxx from 临时表 where xxx进行更新
4.需要插入的dataframe mappartitions,将它写成文件,然后copy到表中
tips:
1.四张表都属于频繁更新表,故fillfactor要设小一些,方便HOT更新
2.update xxx set xxx from 临时表 where xxx进行更新,此方法是最快的更新方式,比batch update还要快
3.“千万不要相信先删除索引,再插入数据,再重建索引”这样的话,大数据量表上重建索引的速度醉人
4.每一张表做完update+copy后,运行vacuum analyze释放空间以及更新执行规划----当然,请另起一个线程做这个事情:)
速度那么就妥妥的稳过4000+/s了:
另外,以前dataframe都是转成javaRDD再做mapPartitions的,其实dataframe可以直接做mappartitons,写法如下:
1.AbstractFunction没有序列化,所以需要自己写个Function继承AbstractFunction并且implements Serializable
例如:
public abstract class MyFunction1<T1,T2> extends scala.runtime.AbstractFunction1<scala.collection.Iterator<Row>, scala.collection.Iterator<Row>> implements Serializable{
}
2.dataframe mappartition
scala.reflect.ClassTag<org.apache.spark.sql.Row> curClassTag = scala.reflect.ClassTag$.MODULE$.apply(org.apache.spark.sql.Row.class);
relateUpdateDf.mapPartitions(new MyFunction1<scala.collection.Iterator<Row>, scala.collection.Iterator<Row>>() {
public scala.collection.Iterator<Row> apply(scala.collection.Iterator<Row> v1) {
............
更加详尽的测试结果:
构造的数据为:
以0.5的概率发送旧数据---触发两张日归并表、事件表、事件详情表更新
以0.5的概率发送新数据---每5分钟触发10万不重复事件id与30万不重复源IP流量,触发两张日归并表、事件表、事件详情表写入
原始日志量为120万/5分钟
一个小时的监控截图:
从图中可以看出,除了19:20:00左右,yarn出现卡顿现象时,有两个task分别高达4.1和8.6分钟,速度降为2400~4000/s时。
其余的时间均为4000+/s,比较平稳。
- 事件生成JOB调优笔记(spark streaming)
- [spark streaming] 动态生成 Job 并提交执行
- spark-streaming系列------- 4. Spark-Streaming Job的生成和执行
- 6 Spark Streaming Job思考
- Spark Streaming 2.0 runDummySpark Job
- spark streaming job 耗时监控
- Spark 定制版:006~Spark Streaming源码解读之Job动态生成和深度思考
- Spark Streaming生成RDD并执行Spark Job源码内幕解密
- Spark Streaming生成RDD并执行Spark Job源码内幕解密
- spark-streaming系列------- 1. spark-streaming的Job调度 上
- spark-streaming系列------- 2. spark-streaming的Job调度 下
- 第6课:Spark Streaming源码解读之Job动态生成和深度思考
- 第6课:Spark Streaming源码解读之Job动态生成和深度思考
- 6.Spark streaming技术内幕 : Job动态生成原理与源码解析
- 第6课:Spark Streaming源码解读之Job动态生成和深度思考
- Spark Streaming源码解读之Job动态生成和深度思考
- Spark streaming技术内幕6 : Job动态生成原理与源码解析
- Spark streaming源码分析之Job动态生成原理与源码解析
- pixhawk commander--navigator--modules之间的联系
- 三大特性--封装
- PostGIS中dbf file (.dbf) can not be opened.shapefile import failed
- iOS应用架构谈 网络层设计方案
- 最简单的基于libVLC的例子:最简单的基于libVLC的视频播放器
- 事件生成JOB调优笔记(spark streaming)
- 类选择器和所作用的标签一起写为什么不起作用?
- ffmeg 反交错
- Android Tween动画之RotateAnimation实现图片不停旋转
- HDU 5795 A Simple Nim(SG打表找规律)
- ac自动机学习
- Qt正则表达式从字符串中取出手机号码
- 文件相关函数
- 1027. Colors in Mars (20)-PAT甲级真题