spark并发读mysql_spark jdbc读取并发度优化

本文介绍了在Spark中使用jdbc读取MySQL数据时,如何通过调整并发度优化性能。详细讲解了三种方法:1) 单partition无并发,2) 根据Long类型字段分区,3) 根据任意类型字段分区。并给出实际案例,展示不同并发度对3000万数据量表count操作的影响,强调合理设置并发度能显著提升数据处理速度,但过高可能导致数据库压力过大。
摘要由CSDN通过智能技术生成

很多人在spark中使用默认提供的jdbc方法时,在数据库数据较大时经常发现任务 hang 住,其实是单线程任务过重导致,这时候需要提高读取的并发度。

下文以 mysql 为例进行说明。

在spark中使用jdbc

在 spark-env.sh 文件中加入:

export SPARK_CLASSPATH=/path/mysql-connector-java-5.1.34.jar

任务提交时加入:

--jars /path/mysql-connector-java-5.1.34.jar

1. 单partition(无并发)

调用函数

def jdbc(url: String, table: String, properties: Properties): DataFrame

使用:

val url = "jdbc:mysql://mysqlHost:3306/database"val tableName= "table"

//设置连接用户&密码

val prop = newjava.util.Properties

prop.setProperty("user","username")

prop.setProperty("password","pwd")//取得该表数据

val jdbcDF =sqlContext.read.jdbc(url,tableName,prop)//一些操作

....

查看并发度

jdbcDF.rdd.partitions.size # 结果返回 1

该操作的并发度为1,你所有的数据都会在一个partition中进行操作,意味着无论你给的资源有多少,只有一个task会执行任务,执行效率可想而之,并且在稍微大点的表中进行操作分分钟就会OOM。

更直观的说法是,达到千万级别的表就不要使用该操作,count操作就要等一万年,no zuo no die ,don’t to try !

WARN TaskSetManager: Lost task 0.0 in stage 6.0 (TID 56, spark047219):

java.lang.OutOfMemoryError: GC overhead limit exceeded

at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3380)

2. 根据Long类型字段分区

调用函数

def jdbc(

url: String,

table: String,

columnName: String, # 根据该字段分区,需要为整形,比如id等

lowerBound: Long, # 分区的下界

upperBound: Long, # 分区的上界

numPartitions: Int, # 分区的个数

connectionProperties: Properties): DataFrame

使用:

val url = "jdbc:mysql://mysqlHost:3306/database"val tableName= "table"val columnName= "colName"val lowerBound= 1,

val upperBound= 10000000,

val numPartitions= 10,//设置连接用户&密码

val prop = newjava.util.Properties

prop.setProperty("user","username")

prop.setProperty("password","pwd")//取得该表数据

val jdbcDF =sqlContext.read.jdbc(url,tableName,columnName,lowerBound,upperBound,numPartitions,prop)//一些操作

....

查看并发度

jdbcDF.rdd.partitions.size # 结果返回 10

该操作将字段 colName 中1-10000000条数据分到10个partition中,使用很方便,缺点也很明显,只能使用整形数据字段作为分区关键字。

3000w数据的表 count 跨集群操作只要2s。

3. 根据任意类型字段分区

调用函数

jdbc(

url: String,

table: String,

predicates: Array[String],

connectionProperties: Properties): DataFrame

下面以使用最多的时间字段分区为例:

val url = "jdbc:mysql://mysqlHost:3306/database"val tableName= "table"

//设置连接用户&密码

val prop = newjava.util.Properties

prop.setProperty("user","username")

prop.setProperty("password","pwd")/**

* 将9月16-12月15三个月的数据取出,按时间分为6个partition

* 为了减少事例代码,这里的时间都是写死的

* modified_time 为时间字段*/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(modified_time as date) >= date '$start'" + s"AND cast(modified_time as date) <= date '$end'"}//取得该表数据

val jdbcDF =sqlContext.read.jdbc(url,tableName,predicates,prop)//一些操作

....

查看并发度

jdbcDF.rdd.partitions.size # 结果返回 6

该操作的每个分区数据都由该段时间的分区组成,这种方式适合各种场景,较为推荐。

结语

以 mysql 3000W 数据量表为例,单分区count,僵死若干分钟报OOM。

分成5-20个分区后,count 操作只需要 2s

高并发度可以大幅度提高读取以及处理数据的速度,但是如果设置过高(大量的partition同时读取)也可能会将数据源数据库弄挂。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值