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

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



一、单分区模式

函数:

  1. def jdbc(url: String, table: String, properties: Properties): DataFrame

使用示例:

  1. val url = "jdbc:mysql://mysqlHost:3306/database"
  2. val tableName = "table"
  3. // 设置连接用户&密码
  4. val prop = new java.util.Properties
  5. prop.setProperty("user","username")
  6. prop.setProperty("password","pwd")
  7. // 取得该表数据
  8. val jdbcDF = sqlContext.read.jdbc(url,tableName,prop)
  9. // 一些操作
  10. ....

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

  1. jdbcDF.rdd.partitions.size=1

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

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

函数:

  1. def jdbc(
  2. url: String,
  3. table: String,
  4. columnName: String,
  5. lowerBound: Long,
  6. upperBound: Long,
  7. numPartitions: Int,
  8. connectionProperties: Properties): DataFrame

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

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

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

  1. if upperBound-lowerBound >= numPartitions:
  2. jdbcDF.rdd.partitions.size = numPartitions
  3. else
  4. jdbcDF.rdd.partitions.size = upperBound-lowerBound

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

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

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

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

三、高自由度的分区模式

函数:

  1. def jdbc(
  2. url: String,
  3. table: String,
  4. predicates: Array[String],
  5. connectionProperties: Properties): DataFrame

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

  1. /**
  2. * 将近90天的数据进行分区读取
  3. * 每一天作为一个分区,例如
  4. * Array(
  5. * "2015-09-17" -> "2015-09-18",
  6. * "2015-09-18" -> "2015-09-19",
  7. * ...)
  8. **/
  9. def getPredicates = {
  10. val cal = Calendar.getInstance()
  11. cal.add(Calendar.DATE, -90)
  12. val array = ArrayBuffer[(String,String)]()
  13. for (i <- 0 until 90) {
  14. val start = new SimpleDateFormat("yyyy-MM-dd").format(cal.getTime())
  15. cal.add(Calendar.DATE, +1)
  16. val end = new SimpleDateFormat("yyyy-MM-dd").format(cal.getTime())
  17. array += start -> end
  18. }
  19. val predicates = array.map {
  20. case (start, end) => s"gmt_create >= '$start' AND gmt_create < '$end'"
  21. }
  22. predicates.toArray
  23. }
  24. val predicates = getPredicates
  25. //链接操作
  26. ...

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

  1. jdbcDF.rdd.partitions.size = predicates.size

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

四、自定义option参数模式

函数示例:

  1. val jdbcDF = sparkSession.sqlContext.read.format("jdbc")
  2. .option("url", url)
  3. .option("driver", "com.mysql.jdbc.Driver")
  4. .option("dbtable", "table")
  5. .option("user", "user")
  6. .option("partitionColumn", "id")
  7. .option("lowerBound", 1)
  8. .option("upperBound", 10000)
  9. .option("fetchsize", 100)
  10. .option("xxx", "xxx")
  11. .load()

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

五、JDBC To Other Databases

Spark官方API文档:

JDBC To Other Databases

5.1Scala

  1. // Note: JDBC loading and saving can be achieved via either the load/save or jdbc methods
  2. // Loading data from a JDBC source
  3. val jdbcDF = spark.read
  4. .format("jdbc")
  5. .option("url", "jdbc:postgresql:dbserver")
  6. .option("dbtable", "schema.tablename")
  7. .option("user", "username")
  8. .option("password", "password")
  9. .load()
  10. val connectionProperties = new Properties()
  11. connectionProperties.put("user", "username")
  12. connectionProperties.put("password", "password")
  13. val jdbcDF2 = spark.read
  14. .jdbc("jdbc:postgresql:dbserver", "schema.tablename", connectionProperties)
  15. // Specifying the custom data types of the read schema
  16. connectionProperties.put("customSchema", "id DECIMAL(38, 0), name STRING")
  17. val jdbcDF3 = spark.read
  18. .jdbc("jdbc:postgresql:dbserver", "schema.tablename", connectionProperties)
  19. // Saving data to a JDBC source
  20. jdbcDF.write
  21. .format("jdbc")
  22. .option("url", "jdbc:postgresql:dbserver")
  23. .option("dbtable", "schema.tablename")
  24. .option("user", "username")
  25. .option("password", "password")
  26. .save()
  27. jdbcDF2.write
  28. .jdbc("jdbc:postgresql:dbserver", "schema.tablename", connectionProperties)
  29. // Specifying create table column data types on write
  30. jdbcDF.write
  31. .option("createTableColumnTypes", "name CHAR(64), comments VARCHAR(1024)")
  32. .jdbc("jdbc:postgresql:dbserver", "schema.tablename", connectionProperties)

5.2Java

  1. // Note: JDBC loading and saving can be achieved via either the load/save or jdbc methods
  2. // Loading data from a JDBC source
  3. Dataset<Row> jdbcDF = spark.read()
  4. .format("jdbc")
  5. .option("url", "jdbc:postgresql:dbserver")
  6. .option("dbtable", "schema.tablename")
  7. .option("user", "username")
  8. .option("password", "password")
  9. .load();
  10. Properties connectionProperties = new Properties();
  11. connectionProperties.put("user", "username");
  12. connectionProperties.put("password", "password");
  13. Dataset<Row> jdbcDF2 = spark.read()
  14. .jdbc("jdbc:postgresql:dbserver", "schema.tablename", connectionProperties);
  15. // Saving data to a JDBC source
  16. jdbcDF.write()
  17. .format("jdbc")
  18. .option("url", "jdbc:postgresql:dbserver")
  19. .option("dbtable", "schema.tablename")
  20. .option("user", "username")
  21. .option("password", "password")
  22. .save();
  23. jdbcDF2.write()
  24. .jdbc("jdbc:postgresql:dbserver", "schema.tablename", connectionProperties);
  25. // Specifying create table column data types on write
  26. jdbcDF.write()
  27. .option("createTableColumnTypes", "name CHAR(64), comments VARCHAR(1024)")
  28. .jdbc("jdbc:postgresql:dbserver", "schema.tablename", connectionProperties);

5.3Python

  1. # Note: JDBC loading and saving can be achieved via either the load/save or jdbc methods
  2. # Loading data from a JDBC source
  3. jdbcDF = spark.read \
  4. .format("jdbc") \
  5. .option("url", "jdbc:postgresql:dbserver") \
  6. .option("dbtable", "schema.tablename") \
  7. .option("user", "username") \
  8. .option("password", "password") \
  9. .load()
  10. jdbcDF2 = spark.read \
  11. .jdbc("jdbc:postgresql:dbserver", "schema.tablename",
  12. properties={"user": "username", "password": "password"})
  13. # Specifying dataframe column data types on read
  14. jdbcDF3 = spark.read \
  15. .format("jdbc") \
  16. .option("url", "jdbc:postgresql:dbserver") \
  17. .option("dbtable", "schema.tablename") \
  18. .option("user", "username") \
  19. .option("password", "password") \
  20. .option("customSchema", "id DECIMAL(38, 0), name STRING") \
  21. .load()
  22. # Saving data to a JDBC source
  23. jdbcDF.write \
  24. .format("jdbc") \
  25. .option("url", "jdbc:postgresql:dbserver") \
  26. .option("dbtable", "schema.tablename") \
  27. .option("user", "username") \
  28. .option("password", "password") \
  29. .save()
  30. jdbcDF2.write \
  31. .jdbc("jdbc:postgresql:dbserver", "schema.tablename",
  32. properties={"user": "username", "password": "password"})
  33. # Specifying create table column data types on write
  34. jdbcDF.write \
  35. .option("createTableColumnTypes", "name CHAR(64), comments VARCHAR(1024)") \
  36. .jdbc("jdbc:postgresql:dbserver", "schema.tablename",
  37. 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. 基于websocket的netty demo

    前面2文 基于http的netty demo 基于socket的netty demo 讲了netty在http和socket的使用,下面讲讲netty如何使用websocket websocket是h ...

  2. JavaScript AMD模块化规范

    浏览器环境 有了服务器端模块以后,很自然地,大家就想要客户端模块.而且最好两者能够兼容,一个模块不用修改,在服务器和浏览器都可以运行. 但是,由于一个重大的局限,使得CommonJS规范不适用于浏览器 ...

  3. 在.NET Core中使用Channel(一)

    我最近一直在熟悉.net Core中引入的新Channel<T>类型.我想在它第一次发布的时候我了解过它,但是有关文章非常非常少,我不能理解它们与其他队列有什么不同. 在使用了一段时间后, ...

  4. [从源码学设计]蚂蚁金服SOFARegistry 之 服务注册和操作日志

    [从源码学设计]蚂蚁金服SOFARegistry之服务注册和操作日志 目录 [从源码学设计]蚂蚁金服SOFARegistry之服务注册和操作日志 0x00 摘要 0x01 整体业务流程 1.1 服务注 ...

  5. 结合MATLAB、Python、R语言,在求得显著差异的边(节点对)之后,怎么画circle图

                                                            先来看看成果图: OK,开始画图: 实验背景声明:在脑影像分析中,我们首先构建脑网络,然 ...

  6. 记录一次spring与jdk版本不兼容的报错

    由于公司项目是普通的web工程,没有用上maven,所以笔者在jdk1.8版本下运行项目报了这样的错误 [ERROR]: 2020-03-09 09:38:50 [org.springframewor ...

  7. 为什么 TCP 连接的建立需要三次握手

    TCP 的通讯双方需要发送 3 个包(即:三次握手)才能建立连接,本文将通过 3 副图来解释为什么需要 3 次握手才能建立连接. TCP 连接的建立过程本质是通信双方确认自己和对方都具有通信能力的过程 ...

  8. 配置Oracle数据库和监听随Linux系统自启动【转】

     配置Oracle数据库和监听随Linux系统自启动     在某些情况下需要在Linux操作系统上提供一种无人值守的随机启动Oracle的功能,目的也许仅仅是为了帮助那些对Oracle细节非常不关心 ...

  9. 【JavaWeb】jQuery 基础

    jQuery 基础 介绍 顾名思义,它是 JavaScript 和 查询,是辅助 JavaScript 开发的类库. 它的核心思想是 write less, do more. 所以它实现了很多浏览器的 ...

  10. Selenium WebDriver 8大定位方式

    Selenium WebDriver 8大定位方式: driver.find_element_by_id() driver.find_element_by_name() driver.find_ele ...