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 ...
随机推荐
- 基于MaxCompute+开放搜索的电商、零售行业搜索开发实践
简介: 搜索一直是电商行业流量来源的核心入口之一,如何搭建电商行业搜索并提升搜索效果,一直是电商行业开发者努力攻克的难题.基于传统数据库或开源引擎虽然能够搭建基础搜索服务,但随着商品数据的增多和业务 ...
- Mysql8.0在windows系统安装一直卡在Starting the server的解决方案
报错:Beginning configuration step: Starting Server Attempting to start service MySQL80 一直卡在这里,手动启动服务也起 ...
- JavaScript中对数组.map()、some()、every()、filter()、forEach的区别
1.区别说明 共同点: 不会对原数组发生修改,而是返回新的变量,用变量接收. 不同点: 1.some():返回一个Boolean类型变量,判断是否有元素符合func条件 2.every():返回一个B ...
- 那什么是URL、URI、URN?
URI Uniform Resource Identifier 统一资源标识符 URL Uniform Resource Locator 统一资源定位符 URN Uniform Resource Na ...
- Unity Shader中的常见流控制指令
在Shader中处理流控制语句时,常加上一些宏去处理流控制指令.例如: [UNITY_UNROLL] for (int i = 0; i < 10; i++) { //do something. ...
- ETSI GS MEC 014,UE 标识 API
目录 文章目录 目录 版本 功能理解 UML UE Identity tag registration UE Identity tag de-registration API Definition U ...
- Vue+springboot集成PageOffice实现在线编辑Word、excel文档
说明: PageOffice是一款在线的office编辑软件,帮助Web应用系统或Web网站实现用户在线编辑Word.Excel.PowerPoint文档.可以完美实现在线公文流转,领导批阅,盖章.可 ...
- go高并发之路——缓存穿透、缓存雪崩
缓存击穿.缓存穿透.缓存雪崩是使用Redis的三个经典问题,上篇文章讲了缓存击穿,今天就讲下剩下的两个问题. 一.缓存穿透 定义:缓存穿透是指查询一个根本不存在的数据,缓存层和DB层都不会命中.这样缓 ...
- zfile 在线云盘、网盘、OneDrive、云存储、私有云、对象存储、h5ai、上传、下载
基于 Java 的在线网盘程序,支持对接 S3.OneDrive.SharePoint.又拍云.本地存储.FTP.SFTP 等存储源,支持在线浏览图片.播放音视频,文本文件.Office.obj(3d ...
- Windows库链接报错
问题回溯 今天拿到别人已经编译好的库,发现在链接的时候出现了报错 [9/9 12.7/sec] Linking CXX shared module bin\plugins\AsensingPlugin ...