记录Spark执行指定日期区间的SQL小任务

200 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情

背景

使用Hive执行sql的时候执行太慢,所以使用spark执行,效率大大提高,有需求执行几年或者某段时间的sql任务,如果直接指定长时间会任务失败,分多个又需要多次提交,所以写一个小任务自己使用

代码解析

环境初始化

val builder: SparkSession.Builder = SparkSession.builder.enableHiveSupport 
val parameterMap: mutable.Map[String, String] = paramsMap.asScala 
if(parameterMap != null){ 
    for ((k, v) <- parameterMap) { 
        builder.config(k, v) 
    }
} 
val spark: SparkSession = builder.getOrCreate

参数配置

val paramsMap = new util.HashMap[String, String]()
//是否开启动态资源配置,根据工作负载来衡量是否应该增加或减少executor,默认false
paramsMap.put("spark.dynamicAllocation.enabled", "false")
//在DML/DDL中是否支持动态分区,默认false
paramsMap.put("hive.exec.dynamic.partition", "true")
//默认strict,在strict模式下,动态分区的使用必须在一个静态分区确认的情况下,其他分区可以是动态
paramsMap.put("hive.exec.dynamic.partition.mode", "nonstrict")
//每个mapper/reducer节点可以创建的最大动态分区数,默认100
paramsMap.put("hive.exec.max.dynamic.partitions.pernode", "10000")
//动态分区的上限,默认1000
paramsMap.put("hive.exec.max.dynamic.partitions", "10000")
//spark.sql.warehouse.dir是spark.sql运行hive的默认路径,如果想运行在hdfs上,可以通过这个配置项来实现,但是一定要是部署好的hive才会生效,spark内置的hive是会将元信息放到spark_home/bin目录下。 //spark.sql.warehouse.dir指定的就是数据的存放位置
paramsMap.put("spark.sql.warehouse.dir","hdfs://xxx/hive/warehouse")
//本地测试
//paramsMap.put("spark.master", "local")

执行过程

//参数判断
if (null == args || args.length != 3) {
  throw new Exception("args error " + JSON.toJSONString(args, SerializerFeature.EMPTY: _*))
}

logger.info("args :" + JSON.toJSONString(args, SerializerFeature.EMPTY: _*))
//自己使用所以没有进行严格的参数校验
val startDate = args(0)
val endDate = args(1)
val initSql = args(2)

//获取日期区间
val dateList: mutable.ListBuffer[String] = getBetweenDates(startDate, endDate)
//遍历执行任务
for (elem <- dateList) {
  logger.info("elem:" + elem)
  val sql: String = HiveUtils.hiveStaticdateReplace(initSql, elem)
  logger.info(s"exec sql:$sql")
  val start: Long = System.currentTimeMillis()
  try {
    spark.sql(sql).show(100)
  } catch {
    case e: Exception =>
      throw e
  } finally {
    val end: Long = System.currentTimeMillis()
    logger.info(s"exec sql:$sql  total_time:${(end - start) / 1000d}s")
  }

}

关闭任务

spark.close()

日期工具方法

def getBetweenDates(start: String, end: String): mutable.ListBuffer[String] = {
  val startData = new SimpleDateFormat("yyyy-MM-dd").parse(start); //定义起始日期
  val endData = new SimpleDateFormat("yyyy-MM-dd").parse(end); //定义结束日期

  val dateFormat: SimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd")
  var buffer = new mutable.ListBuffer[String]()

  buffer += dateFormat.format(startData.getTime())
  val tempStart = Calendar.getInstance()

  tempStart.setTime(startData)
  tempStart.add(Calendar.DAY_OF_YEAR, 1)

  val tempEnd = Calendar.getInstance()
  tempEnd.setTime(endData)
  while (tempStart.before(tempEnd)) {
    // result.add(dateFormat.format(tempStart.getTime()))
    buffer += dateFormat.format(tempStart.getTime())
    tempStart.add(Calendar.DAY_OF_YEAR, 1)
  }
  buffer += dateFormat.format(endData.getTime())
  logger.error(buffer.toString)
  buffer
}

日期替换方法

/**
 * 替换hive sql日期
 */
def hiveStaticdateReplace(sql: String, staticdate: String): String = {
  sql.replace("current_date()", s"'${staticdate}'").
    replace("CURRENT_DATE()", s"'${staticdate}'").
    replace("current_date", s"'${staticdate}'").
    replace("CURRENT_DATE", s"'${staticdate}'")
}

任务提交

spark-submit --master yarn --deploy-mode cluster --class com.xxx.xxx --driver-cores 1 --driver-memory 1G --num-executors 1 --executor-cores 1 --executor-memory 1G --name BatchExecHiveSql --queue xxx.xxx  xxx.jar  2021-01-01 2022-05-31 "insert overwrite table test.xxx partition(staticdate) select * from test.xxx where staticdate = CURRENT_DATE"