Spark SQL支持数据源使用JDBC从其他数据库读取数据。 与使用JdbcRDD相比,应优先使用此功能。 这是因为结果以DataFrame的形式返回,并且可以轻松地在Spark SQL中进行处理或与其他数据源合并。 JDBC数据源也更易于从Java或Python使用,因为它不需要用户提供ClassTag。 (请注意,这与Spark SQL JDBC服务器不同,后者允许其他应用程序使用Spark SQL运行查询)。

首先,您需要在spark类路径上包含特定数据库的JDBC驱动程序。

例如,要从Spark Shell连接到postgres,您可以运行以下命令:

bin/spark-shell --driver-class-path postgresql-9.4.1207.jar --jars postgresql-9.4.1207.jar
  • Spark读取关系型数据库,官方有API接口,如下:

    ①、SparkSession.read.jdbc(url, table, properties)

    ②、SparkSession.read.jdbc(url, table, columnName, lowerBound, upperBound, numPartitions, connectionProperties)

    ③、SparkSession.read.jdbc(url, table, predicates, connectionProperties)
  1. 单partition方式:使用如下函数
def jdbc(url: String, table: String, properties: Properties): DataFrame

例子:

val url = "jdbc:mysql://mysqlHost:3306/database"
val tableName = "table" // 设置连接用户&密码
val prop = new java.util.Properties
prop.setProperty("user","username")
prop.setProperty("password","pwd") // 取得该表数据
val jdbcDF = spark.read.jdbc(url,tableName,prop) // 一些操作
jdbcDF.write.mode..

查看并发度

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

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

更直观的说法是,达到千万级别的表就不要使用该操作,count操作就要等一万年,亲测4个小时 !

  1. 根据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 = new java.util.Properties
prop.setProperty("user","username")
prop.setProperty("password","pwd") // 取得该表数据
val jdbcDF = spark.read.jdbc(url,tableName,columnName,lowerBound,upperBound,numPartitions,prop) // 一些操作
....

查看并发度

jdbcDF.rdd.partitions.size # 结果返回 10
该操作将字段 colName 中1-10000000条数据分到10个partition中,使用很方便,缺点也很明显,只能使用整形数据字段作为分区关键字。
  1. 根据任意类型字段分区

    调用函数为
jdbc(
url: String,
table: String,
predicates: Array[String],
connectionProperties: Properties): DataFrame

例子:

val url = "jdbc:mysql://localhost:3306/db"
val tableName = "tablename" // 设置连接用户&密码
val prop = new java.util.Properties
prop.setProperty("user","mysql")
prop.setProperty("password","123456") val predicates =
Array(
"2018-10-01" -> "2018-11-01",
"2018-11-02" -> "2018-12-01",
"2018-12-02" -> "2019-01-01",
"2019-02-02" -> "2019-03-01",
"2019-03-02" -> "2019-04-01",
"2019-04-02" -> "2019-05-01",
"2019-05-02" -> "2019-06-01",
"2019-06-02" -> "2019-07-01",
"2019-07-02" -> "2019-08-01",
"2019-08-02" -> "2019-09-01",
"2019-09-02" -> "2019-10-01",
"2019-10-02" -> "2019-11-01"
).map {
case (start, end) =>
s"cast(txntime as date) >= date '$start' " + s"AND cast(txntime as date) <= date '$end'"
} // 取得该表数据
val jdbcDF = spark.read.jdbc(url, tableName, predicates, prop)
// 写入到hive表
jdbcDF.write.partitionBy().mode("overwrite").format("orc")
.saveAsTable("db.tableName")

一千万级别数据实测2.4min左右导入完成。

  1. limit分页分区

    依旧采用上述函数,但是partitions做了修改,例子:

val url = "jdbc:mysql://localhost:3306/db"
val tableName = "tablename" // 设置连接用户&密码
val prop = new java.util.Properties
prop.setProperty("user","mysql")
prop.setProperty("password","123456") def getPartition(count:Int) = {
val step = count / 10
Range(0, count, step).map(x =>{
(x, step)
}).toArray
}
val partitions = getPartition(10000000)
.map {
case (start,end) => s"1=1 limit ${start},${end}"
} // 取得该表数据
val jdbcDF = spark.read.jdbc(url, tableName, partitions, prop)
// 写入到hive表
jdbcDF.write.partitionBy().mode("overwrite").format("orc")
.saveAsTable("db.tableName")

实际测试效果和上面的差不多,区别是这里不需要字段有特殊的要求,对行数做处理就行啦。

Spark使用jdbc时的并行度的更多相关文章

  1. spark之JDBC开发(实战)

    一.概述 Spark Core.Spark-SQL与Spark-Streaming都是相同的,编写好之后打成jar包使用spark-submit命令提交到集群运行应用$SPARK_HOME/bin#. ...

  2. spark之JDBC开发(连接数据库测试)

    spark之JDBC开发(连接数据库测试) 以下操作属于本地模式操作: 1.在Eclipse4.5中建立工程RDDToJDBC,并创建一个文件夹lib用于放置第三方驱动包 [hadoop@CloudD ...

  3. 使用Spring Boot操作Hive JDBC时,启动时报出错误:NoSuchMethodError: org.eclipse.jetty.servlet.ServletMapping.setDef

    使用Spring Boot操作Hive JDBC时,启动时报出错误:NoSuchMethodError: org.eclipse.jetty.servlet.ServletMapping.setDef ...

  4. spark通过JDBC读取外部数据库,过滤数据

    官网链接: http://spark.apache.org/docs/latest/sql-programming-guide.html#jdbc-to-other-databases http:// ...

  5. streaming优化:spark.default.parallelism调整处理并行度

    官方是这么说的: Cluster resources can be under-utilized if the number of parallel tasks used in any stage o ...

  6. spark 操作Hive时遇到的问题

    To adjust logging level use sc.setLogLevel(newLevel). For SparkR, use setLogLevel(newLevel).17/10/14 ...

  7. Spark读写HBase时出现的问题--RpcRetryingCaller: Call exception

    问题描述 Exception in thread "main" org.apache.hadoop.hbase.client.RetriesExhaustedException: ...

  8. Spark实际项目中调节并行度

    实际项目中调节并行度 实际项目中调节并行度 并行度概述 spark架构一览 如果不调节并行度,导致并行度过低,会怎么样? 设置spark作业并行度 小结 并行度概述 其实就是指的是,Spark作业中, ...

  9. Spark执行失败时的一个错误分析

    错误分析 堆栈信息中有一个错误信息:Job aborted due to stage failure: Task 1 in stage 2.0 failed 4 times, most recent ...

随机推荐

  1. 上周 GitHub 热点速览 vol.09:手撕 LeetCode 一日 star 破两千

    作者:HelloGitHub-小鱼干 摘要(用于 公众号/博客园等地方):上周 GitHub 趋势榜相较上上周就如同前故事一般,跌到不行,无论是新晋开源小项,还是坚挺老项目,Star 增长量都不如之前 ...

  2. 压力测试(七)-html可视化压测报告细讲

    1.阿里云Linux服务器 Jmeter压测实战之jtl文件生成和查看 简介: 利用软件从阿里云Centos服务器下载压测报告,讲解Jtl文件,并怎么查看文件 可以通过打开jmeter,新建线程组-& ...

  3. Java Web环境配置

    准备工作 jdk-8u241 apache-tomcat-9.0.31-windows-x64.zip Eclipse IDE for Enterprise Java Developers 关于版本选 ...

  4. Flutter01-学习准备

    1. 简介: Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面. Flutter可以与现有的代码一起工作.在全世界,Flutter正在被越来越多的开发者和 ...

  5. 使用QT绘制一个多边形

    目录 1. 概述 2. 实现 2.1. 代码 2.2. 解析 3. 结果 1. 概述 可以通过QT的重绘事件和鼠标事件来绘制多边形,最简单的办法就是在继承QWidget的窗体中重写paintEvent ...

  6. 浏览器内核之 HTML 解释器和 DOM 模型

    微信公众号:爱写bugger的阿拉斯加如有问题或建议,请后台留言,我会尽力解决你的问题. 前言 此文章是我最近在看的[WebKit 技术内幕]一书的一些理解和做的笔记.而[WebKit 技术内幕]是基 ...

  7. 用CSS3实现钟表效果

    背景:最近在学习CSS3,看到了一个小案例,通过自己的学习,动手实现了它,现在把它分享出来. 效果图 实现过程 1.首先我们需要在页面中写出一个静态的钟表效果.首先我们需要一个表盘div wrap 对 ...

  8. Java 八种基本类型和基本类型封装类

    1.首先,八种基本数据类型分别是:int.short.float.double.long.boolean.byte.char:   它们的封装类分别是:Integer.Short.Float.Doub ...

  9. Could not find a valid gem 'redis' (= 0)

    Could not find a valid gem 'redis' (= 0) 报错详情如下: ERROR: Could not find a valid gem 'redis' (>= 0) ...

  10. PHP中elseif与else if的区别

    在PHP中,正常情况下elseif和else if的用法及效果是一样的,但在使用带冒号的if语句时(也就是php替代)情况会有点不一样,比如: <?php  /*正确的写法*/  $a = 1; ...