DataFrame与数据库的相互转化
在Spark中,Dataframe简直可以称为内存中的文本文件。
就像在电脑上直接操作txt、 csv、 json文件一样简单。
val sparkConf = new SparkConf().setAppName("df2db").setMaster("local[1]")
val sc = new SparkContext(sparkConf)
val sqlContext : SQLContext = new SQLContext(sc)
val df = sqlContext.read.format("csv").option("header","true").load("D:\\spark test\\123")
val snapTable = "env0y"
df.registerTempTable(snapTable)
以上寥寥数语就把一个csv文件转为DataFrame并注册为一张临时表了,这时候就可以像操作数据库表一样操作这个snapTable了:
val sql = "SELECT * FROM " + snapTable
val dfTmp = this.sqlContext.sql(sql)
这样写代码方便简单,但可惜的是DataFrame毕竟仅仅存在于内存中,我们业务代码只会输出算法里规定的结果
也就是说,假如结果出错,不好定位到底是DataFrame本身数据有误,还是代码中的SQL写错了。。。
假如能随时随地操作DataFrame就好了,怎么办呢?
把DataFrame保存到真实的数据库去:
import java.util.Properties
val connectionUrl = "jdbc:sqlserver://10.119.46.153:1433"
val table = "Nettransmit.dbo.df2mssql"
val prop = new Properties()
prop.put("JDBC.Driver","com.microsoft.sqlserver.jdbc.SQLServerDriver")
prop.put("user", "sa")
prop.put("password", "ibas.1597")
val dfWriter = df.write.mode(SaveMode.Overwrite).jdbc(connectionUrl, table, prop)
这下好了,如果计算出错了,我们直接连上数据库几条sql就能debug个八九不离十。
唯一要注意的是,DataFrame to Database不是业务要求,所以上面的代码只能在开发模式或者测试模式的时候存在,正式发布版不应该出现
既然可以写进去,自然也可以读出来:
//SqlServer 2 Dataframe
val dfviatable = sqlContext.read.jdbc(connectionUrl,table,prop)
dfviatable.show(10)
以上,DataFrame和数据库之间的极简交互就完成了,但如果业务中真的有读写数据库的需求了,性能问题可能会成为瓶颈,要注意的。
接下来是那么一点点优化。
从csv到DataFrame,我们使用df.printSchema()语句可以在控制台看到类似下面的输出:
root
|-- IMSI: string (nullable = true)
|-- UserType: string (nullable = true)
|-- Total PS Traffic(KB): string (nullable = true)
|-- Total Online Time(s): string (nullable = true)
|-- Total CS Traffic (ERL): string (nullable = true)
|-- Brand: string (nullable = true)
|-- Series: string (nullable = true)
|-- OS: string (nullable = true)
|-- Type: string (nullable = true)
|-- FDD LTE: string (nullable = true)
|-- TDD LTE: string (nullable = true)
|-- Only Report 3G Capability: string (nullable = true)
也就是说,写入到数据库之后每个字段的类型都是string,这显然是一种浪费。
而且很多值完全可以使用int或者double或者bool类型。
怎么办呢?得修改数据库的“方言”,就像在c++中std::locale 建立本地规则一样。
为了方便起见,封装一下:
import java.io.{File, FileInputStream}
import java.util.Properties
import org.apache.spark.sql.jdbc.{JdbcDialect, JdbcDialects, JdbcType}
import org.apache.spark.sql.types._
import org.apache.spark.sql.{DataFrame, SaveMode}
/**
* Created by env0y on 2017/11/24.
*/
object dataframe2db {
def df2db(df: DataFrame,table: String,properties: String) = {
try{
val is = new FileInputStream(new File(properties))
val prop = new Properties()
prop.load(is)
val url = String.valueOf(prop.get("url"))//
JdbcDialects.registerDialect(SQLServerDialect)
df.write.mode(SaveMode.Overwrite).jdbc(url,table,prop)
is.close()
}
}
val SQLServerDialect = new JdbcDialect {
override def canHandle(url: String): Boolean = url.startsWith("jdbc:jtds:sqlserver") || url.contains("sqlserver")
override def getJDBCType(dt: DataType): Option[JdbcType] = dt match {
case StringType => Some(JdbcType("NVARCHAR(128)", java.sql.Types.VARCHAR))
case BooleanType => Some(JdbcType("BIT(1)", java.sql.Types.BIT))
case IntegerType => Some(JdbcType("INTEGER", java.sql.Types.INTEGER))
case LongType => Some(JdbcType("BIGINT", java.sql.Types.BIGINT))
case DoubleType => Some(JdbcType("DOUBLE PRECISION", java.sql.Types.DOUBLE))
case FloatType => Some(JdbcType("REAL", java.sql.Types.REAL))
case ShortType => Some(JdbcType("INTEGER", java.sql.Types.INTEGER))
case ByteType => Some(JdbcType("INTEGER", java.sql.Types.INTEGER))
case BinaryType => Some(JdbcType("BINARY", java.sql.Types.BINARY))
case DateType => Some(JdbcType("DATE", java.sql.Types.DATE))
case TimestampType => Some(JdbcType("DATE", java.sql.Types.DATE))
// case DecimalType.Fixed(precision, scale) => Some(JdbcType("NUMBER(" + precision + "," + scale + ")", java.sql.Types.NUMERIC))
case t: DecimalType => Some(JdbcType(s"DECIMAL(${t.precision},${t.scale})", java.sql.Types.DECIMAL))
case _ => throw new IllegalArgumentException(s"Don't know how to save ${dt.json} to JDBC")
}
}
}
然后像这样调用:
dataframe2db.df2db(df,"Nettransmit.dbo.df2dbff","D:\\ database.properties")
第三个参数是数据库的属性配置文件,内容类似以下:
#\u5F00\u53D1\u6570\u636E\u5E93
driver=com.microsoft.sqlserver.jdbc.SQLServerDriver
url=jdbc:sqlserver://10.119.46.153:1433;databaseName=TspManagement
username=sa
password=ibas.1597
这时候再去观察从DataFrame写入到数据库中表会发现,字段属性都变成NVARCHAR(128)了~~
另外,直接修改DataFrame里面的Schema类型也很简单:
val df1 = df.withColumn("Only Report 3G Capability",col("Only Report 3G Capability").cast(DataTypes.FloatType))
df1.printSchema()
就这些,以上Spark的版本是1.6. 涉及的数据库是sqlServer.
DataFrame与数据库的相互转化的更多相关文章
- spark RDD,DataFrame,DataSet 介绍
弹性分布式数据集(Resilient Distributed Dataset,RDD) RDD是Spark一开始就提供的主要API,从根本上来说,一个RDD就是你的数据的一个不可变的分布式元素集合,在 ...
- r语言与dataframe
什么是DataFrame 引用 r-tutor上的定义: DataFrame 是一个表格或者类似二维数组的结构,它的各行表示一个实例,各列表示一个变量. 没错,DataFrame就是类似于Excel表 ...
- RDD、DataFrame和DataSet的区别
原文链接:http://www.jianshu.com/p/c0181667daa0 RDD.DataFrame和DataSet是容易产生混淆的概念,必须对其相互之间对比,才可以知道其中异同. RDD ...
- RDD、DataFrame和DataSet
简述 RDD.DataFrame和DataSet是容易产生混淆的概念,必须对其相互之间对比,才可以知道其中异同:DataFrame多了数据的结构信息,即schema.RDD是分布式的 Java对象的集 ...
- HibernateTools实现pojo类 数据库schma mapping映射的相互转换 二
接着上一篇博客:HibernateTools实现pojo类 数据库schma mapping映射的相互转换 思路二:由数据库表,生成Mapping映射文件和POJO类. 尽管能够实现,但个人觉着先设计 ...
- SparkSQL 中 RDD 、DataFrame 、DataSet 三者的区别与联系
一.SparkSQL发展: Shark是一个为spark设计的大规模数据仓库系统,它与Hive兼容 Shark建立在Hive的代码基础上,并通过将Hive的部分物理执行计划交换出来(by s ...
- Sprk SQL
一.Spark SQL概述 1.Spark SQL的前生今世 Shark是一个为Spark设计的大规模数据仓库系统,它与Hive兼容.Shark建立在Hive的代码基础上,并通过将Hive的部分物理 ...
- spark 三种数据集的关系(二)
一个Dataset是一个分布式的数据集,而且它是一个新的接口,这个新的接口是在Spark1.6版本里面才被添加进来的,所以要注意DataFrame是先出来的,然后在1.6版本才出现的Dataset,提 ...
- python数据分析入门学习笔记
学习利用python进行数据分析的笔记&下星期二内部交流会要讲的内容,一并分享给大家.博主粗心大意,有什么不对的地方欢迎指正~还有许多尚待完善的地方,待我一边学习一边完善~ 前言:各种和数据分 ...
随机推荐
- commons.dbutils 的使用列子
c0p3的导入请参考前文 https://www.cnblogs.com/appium/p/10183016.html JdbcUtils: package cn.itcast.jdbc; impor ...
- vue小白学习笔记
<div id="div"> <h2>{{ key }}</h2> </div> new Vue ({ el : "#di ...
- codeforces 427D Match & Catch(后缀数组,字符串)
题目 参考:http://blog.csdn.net/xiefubao/article/details/24934617 题意:给两个字符串,求一个最短的子串.使得这个子串在两个字符串中出现的次数都等 ...
- 多重循环、缓冲区管理、数组(day06)
无法预知的数字叫随机数 rand标准函数可以用来获得随机数 为了使用这个标准函数需要包含stdlib.h头文件 srand标准函数用来设置随机数种子 这个函数把一个整数作为种子使用 不同的种子产生的随 ...
- Linux 中,基础命令 command not found...问题
linux中出现基本命令找不到的问题,经过查找发现是/etc/profile文件出现了问题,解决方案如下: 问题详细: [root@master ~]# ll bash: ls: command no ...
- 了解CSS overflow属性 标签: htmlcss属性 2016-12-31 14:38 75人阅读 评论(1)
overflow属性规定当内容溢出元素框时如何显示. 例:div { width:150px; height:150px; overflow:scroll; } 可能的值有: 值 ...
- 解决@vue/cli 创建项目是安装chromedriver时失败的问题
最近在使用新版vue的命令行工具创建项目时,安装chromedriver老是失败,导致后面的步骤也没有进行.网上搜索了一下,全是使用 工作中常见问题汇总及解决方案 npm install chrome ...
- Oracle数据库的性能调整
oracle是一个高性能数据库软件.用户可以通过参数的调整,达到性能的优化.性能优化主要分为两部分:一是数据库管理员通过对系统参数的调整达到优化的目的,二是开发人员通过对应用程序的优化达到调整的目的. ...
- Spring MVC灵活控制返回json的值(自定义过滤字段)
在使用spring MVC开发过程中,为了提高项目执行效率,所以在一些外键字段的实体中会注解”@ManyToOne(fetch = FetchType.LAZY)”以实现延迟加载的效果. 但是,在使用 ...
- (11)Spring Boot配置ContextPath【从零开始学Spring Boot】
Spring boot默认是/ ,这样直接通过http://ip:port/就可以访问到index页面,如果要修改为http://ip:port/path/ 访问的话,那么需要在Application ...