spark读取MySQL的方式及并发度优化

前段时间用sparksession读取MySQL的一个表的时候,出现耗时长,频繁出现oom等情况,去网上查找了一下,是因为用的默认读取jdbc方式,单线程任务重,所以出现耗时长,oom等现象.这时候需要提高读取的并发度.现简单记录下.
看sparsession DataFrameReader源码,读取jdbc有三个方法重载.

  1. 单partition,无并发def jdbc(url: String, table: String, properties: Properties): DataFrame
    使用,校验
	val url: String = "jdbc:mysql://localhost:3306/testdb"
    val table = "students"
    //连接参数
    val properties: Properties = new Properties()
    properties.setProperty("username","root")
    properties.setProperty("password","123456")
    properties.setProperty("driver","com.mysql.jdbc.Driver")
    val tb_table: DataFrame = sparkSession.read.jdbc(url, table, properties)

查看并发度tb_table.rdd.getNumPartitions #返回结果1
该操作的并发度为1,你所有的数据都会在一个partition中进行操作,意味着无论你给的资源有多少,只有一个task会执行任务,执行效率可想而之,并且在稍微大点的表中进行操作分分钟就会OOM.
2. 根据Long类型字段分区
调用函数

  def jdbc(
  url: String,
  table: String,
  columnName: String,    # 根据该字段分区,需要为整形,比如id等
  lowerBound: Long,      # 分区的下界
  upperBound: Long,      # 分区的上界
  numPartitions: Int,    # 分区的个数
  connectionProperties: Properties): DataFrame

使用,校验

	val url: String = "jdbc:mysql://localhost:3306/testdb"
    val table = "students"
    val colName: String = "id"
    val lowerBound = 1
    val upperBound = 10000
    val numPartions = 10
    val properties: Properties = new Properties()
    properties.setProperty("username","root")
    properties.setProperty("password","123456")
    properties.setProperty("driver","com.mysql.jdbc.Driver")
    val tb_table: DataFrame = sparkSession.read.jdbc(url, table,colName, lowerBound, upperBound, numPartions, properties)

查看并发度tb_table.rdd.getNumPartitions #返回结果10
该操作将字段 colName 中1-10000000条数据分到10个partition中,使用很方便,缺点也很明显,只能使用整形数据字段作为分区关键字
3. 根据任意类型字段分区
调用函数

jdbc(
  url: String,
  table: String,
  predicates: Array[String],
  connectionProperties: Properties): DataFrame

使用,校验

	val url: String = "jdbc:mysql://localhost:3306/testdb"
    val table = "students"
    /**
	* 将9月16-12月15三个月的数据取出,按时间分为6个partition
	* 为了减少事例代码,这里的时间都是写死的
	* sbirthday 为时间字段
	*/
    val predicates =
      Array(
        "2015-09-16" -> "2015-09-30",
        "2015-10-01" -> "2015-10-15",
        "2015-10-16" -> "2015-10-31",
        "2015-11-01" -> "2015-11-14",
        "2015-11-15" -> "2015-11-30",
        "2015-12-01" -> "2015-12-15"
      ).map {
        case (start, end) =>
          s"cast(sbirthday as date) >= date '$start' " + s"AND cast(sbirthday as date) <= date '$end'"
      }
    val properties: Properties = new Properties()
    properties.setProperty("username","root")
    properties.setProperty("password","123456")
    properties.setProperty("driver","com.mysql.jdbc.Driver")
    val tb_table: DataFrame = sparkSession.read.jdbc(url, table,predicates, properties)

查看并发度 tb_table.rdd.getNumPartitions #结果为6

该操作的每个分区数据都由该段时间的分区组成,这种方式适合各种场景,较为推荐。
MySQL单partition,大表极容易出现卡死n分钟oom情况.
分成多个partition后,已极大情况避免该情况发生,但是partition设置过高,大量partition同时读取数据库,也可能将数据库弄挂,需要注意.

参考: spark jdbc(mysql) 读取并发度优化

你可能感兴趣的:(大数据,spark,大数据运维)