一、二、三、四标题原文地址:

简书:wuli_小博:Spark JDBC系列–取数的四种方式



一、单分区模式

函数:

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 = sqlContext.read.jdbc(url,tableName,prop) // 一些操作
....

从入参可以看出,只需要传入JDBC URL、表名及对应的账号密码Properties即可。但是计算此DF的分区数后发现,这种不负责任的写法,并发数是1

jdbcDF.rdd.partitions.size=1

操作大数据集时,spark对MySQL的查询语句等同于可怕的:select * from table; ,而单个分区会把数据都集中在一个executor,当遇到较大数据集时,都会产生不合理的资源占用:MySQL可能hang住,spark可能会OOM,所以不推荐生产环境使用;

二、指定Long型column字段的分区模式

函数:

def jdbc(
url: String,
table: String,
columnName: String,
lowerBound: Long,
upperBound: Long,
numPartitions: Int,
connectionProperties: Properties): DataFrame

使用id做分片字段的示例:

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

从入参可以看出,通过指定 id 这个数字型的column作为分片键,并设置最大最小值和指定的分区数,可以对数据库的数据进行并发读取。是不是numPartitions传入多少,分区数就一定是多少呢?其实不然,通过对源码的分析可知:

if upperBound-lowerBound >= numPartitions:
jdbcDF.rdd.partitions.size = numPartitions
else
jdbcDF.rdd.partitions.size = upperBound-lowerBound

拉取数据时,spark会按numPartitions均分最大最小ID,然后进行并发查询,并最终转换成RDD,例如:

入参为:
lowerBound=1, upperBound=1000, numPartitions=10 对应查询语句组为:
JDBCPartition(id < 101 or id is null,0),
JDBCPartition(id >= 101 AND id < 201,1),
JDBCPartition(id >= 201 AND id < 301,2),
JDBCPartition(id >= 301 AND id < 401,3),
JDBCPartition(id >= 401 AND id < 501,4),
JDBCPartition(id >= 501 AND id < 601,5),
JDBCPartition(id >= 601 AND id < 701,6),
JDBCPartition(id >= 701 AND id < 801,7),
JDBCPartition(id >= 801 AND id < 901,8),
JDBCPartition(id >= 901,9)

建议在使用此方式进行分片时,需要评估好 numPartitions 的个数,防止单片数据过大;同时需要column字段的索引建立情况,防止查询语句出现慢SQL影响取数效率。

如果column的数字是离散型的,为了防止拉取时出现过多空分区,以及不必要的一些数据倾斜,需要使用特殊手段进行处理,具体可以参考Spark JDBC系列–读取优化。

三、高自由度的分区模式

函数:

def jdbc(
url: String,
table: String,
predicates: Array[String],
connectionProperties: Properties): DataFrame

使用给定分区数组的示例:

  /**
* 将近90天的数据进行分区读取
* 每一天作为一个分区,例如
* Array(
* "2015-09-17" -> "2015-09-18",
* "2015-09-18" -> "2015-09-19",
* ...)
**/
def getPredicates = { val cal = Calendar.getInstance()
cal.add(Calendar.DATE, -90)
val array = ArrayBuffer[(String,String)]()
for (i <- 0 until 90) {
val start = new SimpleDateFormat("yyyy-MM-dd").format(cal.getTime())
cal.add(Calendar.DATE, +1)
val end = new SimpleDateFormat("yyyy-MM-dd").format(cal.getTime())
array += start -> end
}
val predicates = array.map {
case (start, end) => s"gmt_create >= '$start' AND gmt_create < '$end'"
} predicates.toArray
} val predicates = getPredicates
//链接操作
...

从函数可以看出,分区数组是多个并行的自定义where语句,且分区数为数据size:

jdbcDF.rdd.partitions.size = predicates.size

建议在使用此方式进行分片时,需要评估好 predicates.size 的个数,防止防止单片数据过大;同时需要自定义where语句的查询效率,防止查询语句出现慢SQL影响取数效率。

四、自定义option参数模式

函数示例:

val jdbcDF = sparkSession.sqlContext.read.format("jdbc")
.option("url", url)
.option("driver", "com.mysql.jdbc.Driver")
.option("dbtable", "table")
.option("user", "user")
.option("partitionColumn", "id")
.option("lowerBound", 1)
.option("upperBound", 10000)
.option("fetchsize", 100)
.option("xxx", "xxx")
.load()

从函数可以看出,option模式其实是一种开放接口,spark会根据具体的参数,来决定使用上述三种方式中的某一种。

五、JDBC To Other Databases

Spark官方API文档:

JDBC To Other Databases

5.1Scala

// Note: JDBC loading and saving can be achieved via either the load/save or jdbc methods
// Loading data from a JDBC source
val jdbcDF = spark.read
.format("jdbc")
.option("url", "jdbc:postgresql:dbserver")
.option("dbtable", "schema.tablename")
.option("user", "username")
.option("password", "password")
.load() val connectionProperties = new Properties()
connectionProperties.put("user", "username")
connectionProperties.put("password", "password")
val jdbcDF2 = spark.read
.jdbc("jdbc:postgresql:dbserver", "schema.tablename", connectionProperties)
// Specifying the custom data types of the read schema
connectionProperties.put("customSchema", "id DECIMAL(38, 0), name STRING")
val jdbcDF3 = spark.read
.jdbc("jdbc:postgresql:dbserver", "schema.tablename", connectionProperties) // Saving data to a JDBC source
jdbcDF.write
.format("jdbc")
.option("url", "jdbc:postgresql:dbserver")
.option("dbtable", "schema.tablename")
.option("user", "username")
.option("password", "password")
.save() jdbcDF2.write
.jdbc("jdbc:postgresql:dbserver", "schema.tablename", connectionProperties) // Specifying create table column data types on write
jdbcDF.write
.option("createTableColumnTypes", "name CHAR(64), comments VARCHAR(1024)")
.jdbc("jdbc:postgresql:dbserver", "schema.tablename", connectionProperties)

5.2Java

// Note: JDBC loading and saving can be achieved via either the load/save or jdbc methods
// Loading data from a JDBC source
Dataset<Row> jdbcDF = spark.read()
.format("jdbc")
.option("url", "jdbc:postgresql:dbserver")
.option("dbtable", "schema.tablename")
.option("user", "username")
.option("password", "password")
.load(); Properties connectionProperties = new Properties();
connectionProperties.put("user", "username");
connectionProperties.put("password", "password");
Dataset<Row> jdbcDF2 = spark.read()
.jdbc("jdbc:postgresql:dbserver", "schema.tablename", connectionProperties); // Saving data to a JDBC source
jdbcDF.write()
.format("jdbc")
.option("url", "jdbc:postgresql:dbserver")
.option("dbtable", "schema.tablename")
.option("user", "username")
.option("password", "password")
.save(); jdbcDF2.write()
.jdbc("jdbc:postgresql:dbserver", "schema.tablename", connectionProperties); // Specifying create table column data types on write
jdbcDF.write()
.option("createTableColumnTypes", "name CHAR(64), comments VARCHAR(1024)")
.jdbc("jdbc:postgresql:dbserver", "schema.tablename", connectionProperties);

5.3Python

# Note: JDBC loading and saving can be achieved via either the load/save or jdbc methods
# Loading data from a JDBC source
jdbcDF = spark.read \
.format("jdbc") \
.option("url", "jdbc:postgresql:dbserver") \
.option("dbtable", "schema.tablename") \
.option("user", "username") \
.option("password", "password") \
.load() jdbcDF2 = spark.read \
.jdbc("jdbc:postgresql:dbserver", "schema.tablename",
properties={"user": "username", "password": "password"}) # Specifying dataframe column data types on read
jdbcDF3 = spark.read \
.format("jdbc") \
.option("url", "jdbc:postgresql:dbserver") \
.option("dbtable", "schema.tablename") \
.option("user", "username") \
.option("password", "password") \
.option("customSchema", "id DECIMAL(38, 0), name STRING") \
.load() # Saving data to a JDBC source
jdbcDF.write \
.format("jdbc") \
.option("url", "jdbc:postgresql:dbserver") \
.option("dbtable", "schema.tablename") \
.option("user", "username") \
.option("password", "password") \
.save() jdbcDF2.write \
.jdbc("jdbc:postgresql:dbserver", "schema.tablename",
properties={"user": "username", "password": "password"}) # Specifying create table column data types on write
jdbcDF.write \
.option("createTableColumnTypes", "name CHAR(64), comments VARCHAR(1024)") \
.jdbc("jdbc:postgresql:dbserver", "schema.tablename",
properties={"user": "username", "password": "password"})

Spark JDBC系列--取数的四种方式的更多相关文章

  1. JSP向后台传 递 参 数 的四种方式

    一.通过Form表单提交传值 客户端通过Form表单提交到服务器端,服务器端通过 Java代码 request.getParameter(String xx); 来取得参数(xx)为参数名称.通过ge ...

  2. 160624、Spark读取数据库(Mysql)的四种方式讲解

    目前Spark支持四种方式从数据库中读取数据,这里以Mysql为例进行介绍. 一.不指定查询条件 这个方式链接MySql的函数原型是: 1 def jdbc(url: String, table: S ...

  3. Excel VBA 从外部工作簿取数的5种方法

    '======================================================= '1.循环单元格取数,效率最低,不可取,初学者易犯 '2.区域相等取数 '3.复制粘贴 ...

  4. Spark入Hbase的四种方式效率对比

    一.方式介绍 本次测试一种采用了四种方式进行了对比,分别是:1.在RDD内部调用java API.2.调用saveAsNewAPIHadoopDataset()接口.3.saveAsHadoopDat ...

  5. EF5+MVC4系列(7) 后台SelectListItem传值给前台显示Select下拉框;后台Action接收浏览器传值的4种方式; 后台Action向前台View视图传递数据的四种方式(ViewDate,TempDate,ViewBag,Model (实际是ViewDate.Model传值))

    一:后台使用SelectListItem 传值给前台显示Select下拉框 我们先来看数据库的订单表,里面有3条订单,他们的用户id对应了 UserInfo用户表的数据,现在我们要做的是添加一个Ord ...

  6. iOS 登陆的实现四种方式

    iOS 登陆的实现四种方式 一. 网页加载: http://www.cnblogs.com/tekkaman/archive/2013/02/21/2920218.ht ml [iOS登陆的实现] A ...

  7. .net core 2.x - 缓存的四种方式

    其实这些微软docs都有现成的,但是现在的人想对浮躁些,去看的不会太多,所以这里就再记录下 ,大家一起懒一起浮躁,呵呵. 0.基础知识 通过减少生成内容所需的工作,缓存可以显著提高应用的性能和可伸缩性 ...

  8. C#批量插入数据到Sqlserver中的四种方式

    我的新书ASP.NET MVC企业级实战预计明年2月份出版,感谢大家关注! 本篇,我将来讲解一下在Sqlserver中批量插入数据. 先创建一个用来测试的数据库和表,为了让插入数据更快,表中主键采用的 ...

  9. C#_批量插入数据到Sqlserver中的四种方式

    先创建一个用来测试的数据库和表,为了让插入数据更快,表中主键采用的是GUID,表中没有创建任何索引.GUID必然是比自增长要快的,因为你生成一个GUID算法所花的时间肯定比你从数据表中重新查询上一条记 ...

随机推荐

  1. 在onelogin中使用OpenId Connect Implicit Flow

    目录 简介 OpenId Implicit Flow 创建onelogin的配置 页面的运行和请求流程 关键代码 总结 简介 onelogin支持多种OpenId Connect的连接模式,上一篇文章 ...

  2. vs code编写java

    不知不觉中vs code变得非常强大了,今天小编就分享一下vs code编写java语言.其实除了java语言,还支持很多语言. 首先看下vs code欢迎页面支持哪些语言: 好家伙,支持的东西还真不 ...

  3. fatal error C1045: 编译器限制 : 链接规范嵌套太深

    前言 我相信你是遇到了同样的问题.通过搜索引擎来到这里的.为了不耽误排查问题的时间,我提前说明一下这篇文章所描述的问题范畴: 我遇到的问题和 c++ 模板相关: 如果我减少传递的参数的话,是有可能避免 ...

  4. Angular入门到精通系列教程(7)- 组件(@Component)基本知识

    1. 概述 2. 创建Component 组件模板 视图封装模式 特殊的选择器 :host inline-styles 3. 总结 环境: Angular CLI: 11.0.6 Angular: 1 ...

  5. 剑指offer 面试题7:重建二叉树

    题目描述 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字.例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7, ...

  6. CentOS | python3.7安装指南

    前言: centos系统本身默认安装有python2.x,版本x根据不同版本系统有所不同 可通过 python --V 或 python --version 查看系统自带的python版本 有一些系统 ...

  7. MySQL select join on 连表查询和自连接查询

    连表查询 JOIN ON 操作 描述 inner join 只返回匹配的值 right join 会从右表中返回所有的值, 即使左表中没有匹配 left join 会从左表中返回所有的值, 即使右表中 ...

  8. Spring框架之事务源码完全解析

    Spring框架之事务源码完全解析   事务的定义及特性: 事务是并发控制的单元,是用户定义的一个操作序列.这些操作要么都做,要么都不做,是一个不可分割的工作单位.通过事务将逻辑相关的一组操作绑定在一 ...

  9. wmic 操作文件的datafile

    wmic datafile /?动词有ASSOC,CALL,CREATE,DELETE,GET,LIST 这几个 命令:wmic datafile where "filename='dsc0 ...

  10. ECC 6 debuging中create points

    2013-12-07 今天无意中,发现,在ECC6中debug的时候,创建动态断点,对于command中的delete from语句居然无效,唉 虽然设置了DELETE 和DELETE FROM两个动 ...