前段時間用sparksession讀取MySQL的一個表的時候,出現耗時長,頻繁出現oom等情況,去網上查找了一下,是因爲用的默認讀取jdbc方式,單線程任務重,所以出現耗時長,oom等現象.這時候需要提高讀取的併發度.現簡單記錄下.
看sparsession DataFrameReader源碼,讀取jdbc有三個方法重載.
- 單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同時讀取數據庫,也可能將數據庫弄掛,需要注意.