需求背景:线上千万级数据在离线批处理完成后,需要按指定字段分表推送
方案思路:利用spark读取hive表数据,对数据按字段重分区,分区数据推送至不同的mysql表
代码实现:
1、// 解析配置文件,生成相关配置类
2、spark部分
```-------------------------spark-----------------------------
val broad = sc.broadcast(conf)
val readDf = spark.sql(s"select * from rpt.rpt_abc_d")
val columns = readDf.columns
//重分区
-- readDf.repartition(10,col("key")).foreachpartition( // 该方式的repartition会有问题,并不会按照约定按照字段创建10个分区,我试的时候只有6个分区。后来我改成了rdd的partitionBy
readDf.rdd.map(row=>(row.getAs[Long](”key“),row)).partitionBy(10).foreachpartition( //该方式测试成功
partition=>{
val conf = broad.value
// 建立数据库链接
val conn = DbUtils.getJdbc(conf)
val ps = conn.getpreparestatement("")
val prexSql = DbUtils.getPrexSql(conf)
partition.foreach{
var count=1
row=>{
var sql = ""
//组装sql 类似 insert into table (id,name) values (1,'zhangsan'),(2,'Lisi')。这种效率远比一个个value要来的高
columns.foreach(col=>{
sql = DbUtils.getInsertSql(row.getAs(col))
})
prexSql = prexSql +sql
if(count%10000==0){
ps.addbatch(prexSql)
}
count = count+1
}
}
}
)
总结:
1、mysql的本地load会比上述的insert快很多2-3倍至少,换种思路,可以download到本地,然后进行文件按照key值切割,然后对切割文件load,这个效率高很多,缺点是要down到本地,本地在做切割,在数据量极大的情况下容易内存溢出。原来采用的是这种方式。
2、spark的问题
1、 repartition(partitionExprs : org.apache.spark.sql.Column*) 这个分区不知道为啥不按指定的来
2、对executor参数设置的理解,因为如果有1024个表,不可能开1024个分区。所以要限制executor的数量。
num-executors 直接指定executor的数量
executor-cores 一个executor的cpu core数量
总并发:num-executors*executor-cores,第一次设置上面两参数不起效果,,因为开了下面这个参数dynamic allocation=false
spark.cores.max 是指你的spark程序需要的总核数 网上说 executor 数量 = spark.cores.max/spark.executor.cores 试了下不对,可能打开方式不行。
spark.default.parallelism并行度设置 设置每个stage的默认task数量
提交方式:
spark-submit
–class com.SparkTodb
–master yarn
–conf spark.dynamicAllocation.enabled=false
–conf “spark.driver.extraJavaOptions=-Dlog4j.configuration=file:/root/log4j.properties”
–queue $queue_name
–jars /root/program/env/jars/sparktools-jar-with-dependencies.jar
/root/program/env/jars/sparktools.jar