SparkSQL常用性能优化

一、代码优化

1.在数据统计的时候选择高性能算子。

例如Dataframe使用foreachPartitions将数据写入数据库,不要每个record都去拿一次数据库连接。通常写法是每个partition拿一次数据库连接。

      /**
        * 将统计结果写入MySQL中
        * 代码优化:
        * 在进行数据库操作的时候,不要每个record都去操作一次数据库
        * 通常写法是每个partition操作一次数据库
        **/
      try {
        videoLogTopNDF.foreachPartition(partitionOfRecords => {
          val list = new ListBuffer[DayVideoAccessStat]
          partitionOfRecords.foreach(info => {
            val day = info.getAs[String]("day")
            val cmsId = info.getAs[Long]("cmsId")
            val times = info.getAs[Long]("times")

            list.append(DayVideoAccessStat(day, cmsId, times))
          })
          StatDao.insertDayVideoTopN(list)
        })
      }catch{
        case e:Exception =>e.printStackTrace()
      }
2.写数据库的时候,关闭自动提交,不要每条提交一次,自己手动每个批次提交一次。

 var connection:Connection = null
    var pstmt : PreparedStatement = null
    try{
      connection = MySQLUtils.getConnection()

      connection.setAutoCommit(false)//关闭自动提交
      val sql = "insert into day_video_access_topn_stat(day,cms_id,times) values(?,?,?)"
      pstmt = connection.prepareStatement(sql)

      for(ele <- list){
        pstmt.setString(1,ele.day)
        pstmt.setLong(2,ele.cmsId)
        pstmt.setLong(3,ele.times)
        //加入到批次中,后续再执行批量处理 这样性能会好很多
        pstmt.addBatch()
      }
      //执行批量处理
      pstmt.executeBatch()

      connection.commit() //手工提交

    }catch {
      case e :Exception =>e.printStackTrace()
    }finally {
      MySQLUtils.release(connection,pstmt)
    }
3.复用已有的数据。

三个统计方法都是只要当天的视频数据,所以在调用方法前过滤出当天视频数据,缓存到内存中。

然后传到三个统计方法中使用。

不要在每个统计方法都去做一次相同的过滤。

val logDF = spark.read.format("parquet")
      .load("file:///F:\\mc\\SparkSQL\\data\\afterclean")
    val day = "20170511"


    /**
      * 代码优化:复用已有数据
      * 既然每次统计都是统计的当天的视频,
      * 先把该数据拿出来,然后直接传到每个具体的统计方法中
      * 不要在每个具体的统计方法中都执行一次同样的过滤
      *
      * 用$列名得到列值,需要隐式转换 import spark.implicits._
      * */
    import spark.implicits._
    val dayVideoDF = logDF.filter($"day" ===day&&$"cmsType"==="video")
    /**
      * 将这个在后文中会复用多次的dataframe缓存到内存中
      * 这样后文在复用的时候会快很多
      *
      * default storage level (`MEMORY_AND_DISK`).
      * */
    dayVideoDF.cache()

    //logDF.printSchema()
    //logDF.show()

    StatDao.deletaDataByDay(day)

    //统计每天最受欢迎(访问次数)的TopN视频产品
    videoAccessTopNStatDFAPI(spark,dayVideoDF)

    //按照地势统计每天最受欢迎(访问次数)TopN视频产品 每个地市只要最后欢迎的前三个
    cityAccessTopNStat(spark,dayVideoDF)

    //统计每天最受欢迎(流量)TopN视频产品
    videoTrafficsTopNStat(spark,dayVideoDF)

    //清除缓存
    dayVideoDF.unpersist(true)
二、集群存储格式选择

    列式/行式存储简介

这里建议选择用parquet格式,之前公司中用的也是这种格式。

三、集群压缩格式选择

Hadoop压缩实现分析

Spark中选择用哪个方式压缩文件

SparkSession.builder().config("spark.sql.parquet.compression.codec",snappy).getOrCreate()默认是snappy。

四、参数优化

1.并行度:spark.sql.shuffle.partitions

一个partitions相当于一个task。这是配置当shuffle数据去join或者聚合的时候的partitions的数量。200一般情况下在生产上是不够的,需要做相应的调整。

调整并行度的方式

bin/spark-submit --class XXX.XXX.XX --name XXX --master local[2] --conf spark.sql.shuffle.partitions=230 XXX.jar

2.不必要的情况下,关闭分区字段类型自动推导

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章