背景
1,前几天接了一个公司任务,把数据从Hologres推到Es,因为业务需求需要每十分钟调度一次且往Es写数据之前还要经过一步从Holo的查询关联其他属性。
资源
数据量:result表:(1800万)
配置表:(50万)
spark:Driver:核心数(1个)
内存数(1G)
Excutor:核心数(2个)
数量(2个)
内存数(4G)
出现的问题
程序写完任务运行时间是半小时级别,中间的优化手段,主要有序列化,小表缓存,大表join小表时使用mapjoin,通过casewhen代替join等手段,但是通过SparkUI定位时发现一只有一个task(并行度)为1的job任务时长超过12分钟,且经常出现Excutor宕机的情况,于是开始调整并行度(先一步一步设置了reparation,然后设置全局并行度),但是UI中job的task数量是1,于是开始一步一步本地测试发现全局并行度对read无效。
问题的解决
问题已经定位是read读取数据时并行度是1,如果使用repartition算子 势必会增加shuffle,对性能也会有一定影响,因此问题就集中在了Spark如何多分区读取数据
方法
四个参数:
numPartitions分区数量,可以理解为读取并行度、线程数
partitionColumn 分区字段,必须为数字、日期、时间戳字段
//lowerBound 和 upperBound 仅用于计算每个分区的取数步长,不用于数据过滤
lowerBound 分区字段的最小值
upperBound 分区字段的最大值
原理:
通过数据表的最大最小值以及分区数量,确定每个分区的数据边界
1,确定数据的切分规则,选定表中一列数据为分区字段(分区字段,必须为数字、日期、时间戳字段)作为partitionColumn ,如id
2,根据分区个数numPartitions,可根据机器配置自行决定
3,确定id最大最小值(upperBound 和lowerBound ),select max(id),min(id) from table
配置如下:
spark.read().format("jdbc").option()....
.option("dbtable", tb)
// 以下4个配置项必须同时使用
// 分区数量,可以理解为读取并行度、线程数
.option("numPartitions", partitionNum)
// 分区字段,必须为数字、日期、时间戳字段
.option("partitionColumn", splitCol)
// lowerBound 和 upperBound 仅用于计算每个分区的取数步长,不用于数据过滤
// 分区字段的最小值
.option("lowerBound", minv)
// 分区字段的最大值
.option("upperBound", maxv)
.load()
效果
优化前:30分钟左右
优化后:2分钟
使用限制
1,分区字段,必须为数字、日期、时间戳字段
2,数据如果分布不均匀,会出现数据倾斜,如(1,100000,100001,100002,100003....200000),如果步长是100000,则分区1只有1和100000,而分区2则有100000个数据