spark使用jdbc批次提交方式写入phoniex的工具类
一、需求:spark写入phoniex
二、实现方式
1.官网方式
dataFrame.write
.format("org.apache.phoenix.spark")
.mode("overwrite")
.option("table", table)
.option("zkUrl", zkUrl)
.option("skipNormalizingIdentifier", true)
.save()
这个方式底层是使用MapReduce的RecordWriter实现类PhoenixRecordWriter通过jdbc方式写入


但是默认的batchsize是1000,所以插入速度极慢,但是官网没有说明写入的参数设置,需要去源码里面寻找一下
所以可以通过设置参数来提升速度
.option(PhoenixConfigurationUtil.UPSERT_BATCH_SIZE,batch)
二、自己实现jdbc的通用方式(任何jdbc方式都可以写入)
代码:
object JdbcUtils {
def jdbcBatchInsert(dataFrame: DataFrame, table: String, url: String, pro: Properties, batch: Int): Unit = {
val fields: Array[String] = dataFrame.schema.fieldNames
val schema: Array[StructField] = dataFrame.schema.toArray
val numFields = fields.length
val fieldsSql = fields.map(str => "\"".concat(str).concat("\"")).mkString("(", ",", ")")
val charSql = fields.map(str => "?").mkString(",")
val setters: Array[JDBCValueSetter] = schema.map(f => makeSetter(f.dataType))
val insertSql = s"upsert into $table $fieldsSql values ($charSql) "
System.err.println("插入sql:" + insertSql)
val start = System.currentTimeMillis()
dataFrame.rdd.foreachPartition(partition => {
val connection = DriverManager.getConnection(url, pro)
try {
connection.setAutoCommit(false)
val pstmt: PreparedStatement = connection.prepareStatement(insertSql)
var count = 0
var cnt = 0
partition.foreach(row => {
for (i <- 0 until numFields) {
if (row.isNullAt(i)) {
pstmt.setNull(i + 1, getJdbcType(schema(i).dataType))
} else {
setters(i).apply(pstmt, row, i)
}
}
pstmt.addBatch()
count += 1
if (count % batch == 0) {
pstmt.executeBatch()
connection.commit()
cnt += 1
println(s"${TaskContext.get.partitionId}分区,提交第${cnt}次,${count}tiao")
}
})
pstmt.executeBatch()
connection.commit()
println(s"第${TaskContext.get.partitionId}分区,共提交第${cnt},${count}条")
} finally {
connection.close()
}
})
val end = System.currentTimeMillis()
println(s"插入表$table,共花费时间${(end - start) / 1000}秒")
}
private type JDBCValueSetter = (PreparedStatement, Row, Int) => Unit
/**
* 类型匹配 如果有其他类型 自行添加
*
* @param dataType
* @return
*/
def makeSetter(dataType: DataType): JDBCValueSetter = dataType match {
case IntegerType =>
(stmt: PreparedStatement, row: Row, pos: Int) =>
if (row.isNullAt(pos)) {
stmt.setNull(pos + 1, java.sql.Types.INTEGER)
} else {
stmt.setInt(pos + 1, row.getInt(pos))
}
case LongType =>
(stmt: PreparedStatement, row: Row, pos: Int) =>
stmt.setLong(pos + 1, row.getLong(pos))
case DoubleType =>
(stmt: PreparedStatement, row: Row, pos: Int) =>
stmt.setDouble(pos + 1, row.getDouble(pos))
case FloatType =>
(stmt: PreparedStatement, row: Row, pos: Int) =>
stmt.setFloat(pos + 1, row.getFloat(pos))
case ShortType =>
(stmt: PreparedStatement, row: Row, pos: Int) =>
stmt.setInt(pos + 1, row.getShort(pos))
case ByteType =>
(stmt: PreparedStatement, row: Row, pos: Int) =>
stmt.setInt(pos + 1, row.getByte(pos))
case BooleanType =>
(stmt: PreparedStatement, row: Row, pos: Int) =>
stmt.setBoolean(pos + 1, row.getBoolean(pos))
case StringType =>
(stmt: PreparedStatement, row: Row, pos: Int) =>
stmt.setString(pos + 1, row.getString(pos))
case BinaryType =>
(stmt: PreparedStatement, row: Row, pos: Int) =>
stmt.setBytes(pos + 1, row.getAs[Array[Byte]](pos))
case TimestampType =>
(stmt: PreparedStatement, row: Row, pos: Int) =>
stmt.setTimestamp(pos + 1, row.getAs[java.sql.Timestamp](pos))
case DateType =>
(stmt: PreparedStatement, row: Row, pos: Int) =>
stmt.setDate(pos + 1, row.getAs[java.sql.Date](pos))
case t: DecimalType =>
(stmt: PreparedStatement, row: Row, pos: Int) =>
stmt.setBigDecimal(pos + 1, row.getDecimal(pos))
/* case ArrayType(et, _) =>
// remove type length parameters from end of type name
val typeName = getJdbcType(et, dialect).databaseTypeDefinition
.toLowerCase(Locale.ROOT).split("\\(")(0)
(stmt: PreparedStatement, row: Row, pos: Int) =>
val array = conn.createArrayOf(
typeName,
row.getSeq[AnyRef](pos).toArray)
stmt.setArray(pos + 1, array)*/
case _ =>
(_: PreparedStatement, _: Row, pos: Int) =>
throw new IllegalArgumentException(
s"Can't translate non-null value for field $pos")
}
/**
* sql类型匹配 如果有其他类型 自行添加
*
* @param dt
* @return
*/
private def getJdbcType(dt: DataType): Int = {
dt match {
case IntegerType => java.sql.Types.INTEGER
case LongType => java.sql.Types.BIGINT
case DoubleType => java.sql.Types.DOUBLE
case StringType => java.sql.Types.VARCHAR
case _ => java.sql.Types.VARCHAR
}
}
}
测试:
#config是个map集合要不要都可以
val connectionProperties = new Properties();
connectionProperties.setProperty(QueryServices.MAX_MUTATION_SIZE_ATTRIB, config.getOrDefault("phoenix.mutate.maxSize", "500000")); //改变默认的500000
connectionProperties.setProperty(QueryServices.MUTATE_BATCH_SIZE_BYTES_ATTRIB, config.getOrDefault("phoenix.mutate.batchSizeBytes", "1073741824000")) val batch = config.getOrDefault("phoenix.insert.batchSize", "50000").toInt //调用插入方法
JdbcUtils.jdbcBatchInsert(dataFrame, table, phoenixUrl, connectionProperties, batch);
spark使用jdbc批次提交方式写入phoniex的工具类的更多相关文章
- Java中常用的加密方式(附多个工具类)
一.Java常用加密方式 Base64加密算法(编码方式) MD5加密(消息摘要算法,验证信息完整性) 对称加密算法 非对称加密算法 数字签名算法 数字证书 二.分类按加密算法是否需要key被分为两类 ...
- 【JDBC】学习路径5-提取JDBCUtils工具类
回顾我们上面几节的内容,我们发现重复代码非常多,比如注册驱动.连接.关闭close()等代码,非常繁杂. 于是我们将这些重复的大段代码进行包装.提取成JDBCUtils工具类. 第一章:提取注册连接模 ...
- Spark jdbc postgresql数据库连接和写入操作源码解读
概述:Spark postgresql jdbc 数据库连接和写入操作源码解读,详细记录了SparkSQL对数据库的操作,通过java程序,在本地开发和运行.整体为,Spark建立数据库连接,读取数据 ...
- Spark Standalone与Spark on YARN的几种提交方式
不多说,直接上干货! Spark Standalone的几种提交方式 别忘了先启动spark集群!!! spark-shell用于调试,spark-submit用于生产. 1.spark-shell ...
- Spark jdbc postgresql数据库连接和写入操作源代码解读
概述:Spark postgresql jdbc 数据库连接和写入操作源代码解读.具体记录了SparkSQL对数据库的操作,通过java程序.在本地开发和执行.总体为,Spark建立数据库连接,读取数 ...
- spark之JDBC开发(实战)
一.概述 Spark Core.Spark-SQL与Spark-Streaming都是相同的,编写好之后打成jar包使用spark-submit命令提交到集群运行应用$SPARK_HOME/bin#. ...
- spark之JDBC开发(连接数据库测试)
spark之JDBC开发(连接数据库测试) 以下操作属于本地模式操作: 1.在Eclipse4.5中建立工程RDDToJDBC,并创建一个文件夹lib用于放置第三方驱动包 [hadoop@CloudD ...
- Spark使用jdbc时的并行度
Spark SQL支持数据源使用JDBC从其他数据库读取数据. 与使用JdbcRDD相比,应优先使用此功能. 这是因为结果以DataFrame的形式返回,并且可以轻松地在Spark SQL中进行处理或 ...
- 各种数据库使用JDBC连接的方式
Java数据库连接(JDBC)由一组用 Java 编程语言编写的类和接口组成.JDBC 为工具/数据库开发人员提供了一个标准的 API,使他们能够用纯Java API 来编写数据库应用程序.然而各个开 ...
- spark下使用submit提交任务后报jar包已存在错误
使用spark submit进行任务提交,离线跑数据,提交后的一段时间内可以application可以正常运行.过了一段时间后,就抛出以下错误: org.apache.spark.SparkExcep ...
随机推荐
- 阿里云日志服务SLS,打造云原生时代智能运维
2021年10月21日,阿里云针对企业运维难题,在云栖大会为大家带来了一场<智能运维论坛>的主题演讲.在会上,阿里云资深技术专家.日志服务技术负责人简志提出"云原生时代,企业业 ...
- 多任务多目标CTR预估技术
简介: 多目标(Multi Objective Learning)是MTL中的一种.在业务场景中,经常面临既要又要的多目标问题.而多个目标常常会有冲突.如何使多个目标同时得到提升,是多任务多目标在真 ...
- Sentinel如何持久化数据到Nacos?
默认情况下 Sentinel 只能接收到 Nacos 推送的消息,但不能将自己控制台修改的信息同步给 Nacos,如下图所示: 但是在生成环境下,我们为了更方便的操作,是需要将 Sentinel 控制 ...
- Photoshop批量替换图层的方法
平时做图片,应该有遇到这样的场景,比如P奖状.P邀请函,内容是一样的,但是图片上的名字是不一样的,要是要P100张的话,一个个手动复制改名字肯定会吐血(╯°□°)╯︵ ┻━┻ Photoshop里有个 ...
- vue监听watch
export default { watch:{ showArea(val,_val){ console.log('showAre ...
- Android Studio自强迫升级到4.2版本后调试Native项目时总是卡死问题
原文地址:https://www.zhaimaojun.top/Note/5464968 就在昨天,也就是2021年5月6号,Android Studio强迫用户升级到4.2版本, 原因就是jcent ...
- 04.2 go-admin前后端打包为一个服务上线
目录 一.思路: 二.打包go-admin-ui为静态文件 a.修改配置文件 b.打包 c.复制dist到go-admin的static目录里 三.配置go-admin a.配置路由 b.访问页面 视 ...
- 如何使用go module导入本地包
go module是Go1.11版本之后官方推出的版本管理工具,并且从Go1.13版本开始,go module将是Go语言默认的依赖管理工具. 到今天Go1.14版本推出之后Go modules 功能 ...
- Centos 密码过期问题 password has expired
1.这个主要是由/etc/login.defs参数文件中的一些参数控制的的.它主要用于用户账号限制PASS_MAX_DAYS 60 #密码最大有效期,此处参数PASS_MAX_DAYS为60, ...
- LLM实战:LLM微调加速神器-Unsloth + LLama3
1. 背景 五一结束后,本qiang~又投入了LLM的技术海洋中,本期将给大家带来LLM微调神器:Unsloth. 正如Unsloth官方的对外宣贯:Easily finetune & tra ...