背景

        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个数据