在项目中,遇到一个场景是,需要从Hive数据仓库中拉取数据,进行过滤、裁剪或者聚合之后生成中间结果导入MySQL。

对于这样一个极其普通的离线计算场景,有多种技术选型可以实现。例如,sqoop,MR,HSQL。

我们这里使用的spark,优点来说是两个:一是灵活性高,二是代码简洁。

1)灵活性高

相比sqoop和HSQL,spark可以更灵活的控制过滤和裁剪逻辑,甚至你可以通过外部的配置或者参数,来动态的调整spark的计算行为,提供定制化。

2)代码简洁

相比MR来说,代码量上少了很多。也无需实现MySQL客户端。

我抽象了一下需求,做了如下一个demo。

涉及的数据源有两个:Hive&MySQL;计算引擎:spark&spark-sql。我们的demo中分为两个步骤:

1)从Hive中读取数据,交给spark计算,最终输出到MySQL;

2)从MySQL中读取数据,交给spark计算,最终再输出到MySQL另一张表。

1、 数据准备

创建了Hive外部分区表

关于分区和外部表这里不说了。

CREATE EXTERNAL TABLE `gulfstream_test.accounts`(
`id` string COMMENT '用户id',
`order_id` string COMMENT '订单id',
`status` bigint COMMENT '用户状态',
`count` decimal(,) COMMENT '订单数')
COMMENT '用户信息'
PARTITIONED BY (
`year` string,
`month` string,
`day` string)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
STORED AS INPUTFORMAT
'org.autonavi.udf.CustomInputFormat'
OUTPUTFORMAT
'org.autonavi.udf.CustomHiveOutputFormat'
LOCATION
'hdfs://mycluster-tj/***/acounts'
TBLPROPERTIES (
'LEVEL'='',
'TTL'='',
'last_modified_by'='yangfan',
'last_modified_time'='2017-10-23',
'transient_lastDdlTime'='')

建立分区,并指定分区路径

这里分区使用的年月日三级分区。通过下面的命令将year=2017/month=10/day=23这个Hive分区的数据指向了location=hdfs://mycluster-tj/***/acounts/2017/10/23

hive> alter table gulfstream_test.accounts add partition(year='', month='', day='') location 'hdfs://mycluster-tj/***/acounts/2017/10/23';

查询一下分区是否建立成功

可以看到分区已经有了。

show partitions gulfstream_test.accounts;
OK
partition
year=/month=/day=

上传本地测试数据到hdfs

hadoop fs -put a.txt  hdfs://mycluster-tj/***/acounts/2017/10/23

看一下数据,取了前10行,原谅我数据比较假。

[data_monitor@bigdata-arch-client10 target]$ hadoop fs -cat hdfs://mycluster-tj/***/acounts/2017/10/23/a | head -10

在Hive中,也查一下前10条,是一样的。只是多了分区字段。

hive (default)> select * from gulfstream_test.accounts where year= and month= and day= limit ;
OK
accounts.id accounts.order_id accounts.status accounts.count accounts.year accounts.month accounts.day Time taken: 1.38 seconds, Fetched: row(s)

至此,测试数据准备好了。一共1000000条,1百万。

2、代码

1)POM依赖

可以通过pom依赖来看一下笔者使用的组件版本。

这里就不赘述了。

<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.10</artifactId>
<version>1.6.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-hive_2.10</artifactId>
<version>1.6.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_2.10</artifactId>
<version>1.6.0</version>
<scope>provided</scope>
</dependency>

打包方式

<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<!--这里要替换成jar包main方法所在类 -->
<mainClass>com.kangaroo.studio.algorithms.filter.LoadDB</mainClass> </manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id> <!-- this is used for inheritance merges -->
<phase>package</phase> <!-- 指定在打包节点执行jar包合并操作 -->
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>

2)java spark代码

先贴上代码,再说明

package com.kangaroo.studio.algorithms.filter;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.sql.DataFrame;
import org.apache.spark.sql.SQLContext;
import org.apache.spark.sql.SaveMode;
import org.apache.spark.sql.hive.HiveContext; import java.io.Serializable;
import java.util.Properties; public class LoadDB implements Serializable { private SparkConf sparkConf;
private JavaSparkContext javaSparkContext;
private HiveContext hiveContext;
private SQLContext sqlContext; /*
* 初始化Load
* 创建sparkContext, sqlContext, hiveContext
* */
public LoadDB() {
initSparckContext();
initSQLContext();
initHiveContext();
} /*
* 创建sparkContext
* */
private void initSparckContext() {
String warehouseLocation = System.getProperty("user.dir");
sparkConf = new SparkConf()
.setAppName("from-to-mysql")
.set("spark.sql.warehouse.dir", warehouseLocation)
.setMaster("yarn-client");
javaSparkContext = new JavaSparkContext(sparkConf);
} /*
* 创建hiveContext
* 用于读取Hive中的数据
* */
private void initHiveContext() {
hiveContext = new HiveContext(javaSparkContext);
} /*
* 创建sqlContext
* 用于读写MySQL中的数据
* */
private void initSQLContext() {
sqlContext = new SQLContext(javaSparkContext);
} /*
* 使用spark-sql从hive中读取数据, 然后写入mysql对应表.
* */
public void hive2db() {
String url = "jdbc:mysql://10.93.84.53:3306/big_data?characterEncoding=UTF-8";
String table = "accounts";
Properties props = new Properties();
props.put("user", "root");
props.put("password", "1234");
String query = "select * from gulfstream_test.accounts where year=2017 and month=10 and day=23";
DataFrame rows = hiveContext.sql(query).select("id", "order_id", "status", "count");;
rows.write().mode(SaveMode.Append).jdbc(url, table, props);
} /*
* 使用spark-sql从db中读取数据, 处理后再回写到db
* */
public void db2db() {
String url = "jdbc:mysql://10.93.84.53:3306/big_data?characterEncoding=UTF-8";
String fromTable = "accounts";
String toTable = "accountsPart";
Properties props = new Properties();
props.put("user", "root");
props.put("password", "1234");
DataFrame rows = sqlContext.read().jdbc(url, fromTable, props).where("count < 1000");
rows.write().mode(SaveMode.Append).jdbc(url, toTable, props);
} public static void main(String[] args) {
LoadDB loadDB = new LoadDB();
System.out.println(" ---------------------- start hive2db ------------------------");
loadDB.hive2db();
System.out.println(" ---------------------- finish hive2db ------------------------");
System.out.println(" ---------------------- start db2db ------------------------");
loadDB.db2db();
System.out.println(" ---------------------- finish db2db ------------------------");
}
}

说明:

  • hive2db

核心动作是使用hiveContext.sql(query)执行了hiveSQL,过滤出Hive表中year=2017/month=10/day=23分钟的数据,返回一个DataFrame对象。

DataFrame是spark-sql数据处理的核心。对DataFrame的操作推荐这样一篇博客。你可以去使用这些方法,实现复杂的逻辑。

对DataFrame对象,我们使用了select裁剪了其中4列数据(id, order_id, status, count)出来,不过不裁剪的话,会有7列(加上分区的year,month,day)。

然后将数据以SaveMode.Append的方式,写入了mysql中的accounts表。

SaveMode.Append方式,数据会追加,而不会覆盖。如果想覆盖,还有一个常用的SaveMode.Overwrite。推荐这样一篇博客

最终accounts中的数据有1000000条,百万。

  • db2db

db2db从刚刚生成的MySQL表accounts中读取出数据,也是返回了一个dataframe对象,通过执行where过滤除了其中id<1000的数据,这里正好是1000条。

然后写入了accountsPart。最终accountsPart数据应该有1000条。

3)编译和执行

编译完成后,生成jar包from-to-mysql-1.0-SNAPSHOT-jar-with-dependencies.jar

使用默认参数提交到yarn队列。

spark-submit --queue=root.zhiliangbu_prod_datamonitor from-to-mysql-1.0-SNAPSHOT-jar-with-dependencies.jar 

片刻之后,观察输出。已经全部finish了。

4)查看一下结果

我们到mysql中瞅一瞅。

accounts表

有没有注意到,其实不用建立mysql表!这个过程会自动给你创建,相当于if not exists。

细心的你可能已经注意到了,hive里的string类型,到了MySQL中变成了Text。有个兄弟说,如果你手动创建了表,并且字段设置为String会报错,我没有试,只是记录了一下。

CREATE TABLE `accounts` (
`id` text,
`order_id` text,
`status` bigint(20) DEFAULT NULL,
`count` decimal(16,9) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8

简单看一下里面有多少数据。1百万

MariaDB [big_data]> select count() from accounts ;
+----------+
| count() |
+----------+
| |
+----------+
row in set (0.32 sec)

acountsPart表

 CREATE TABLE `accountsPart` (
`id` text,
`order_id` text,
`status` bigint() DEFAULT NULL,
`count` decimal(,) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8

查看有多少数据,1000条,果然是没有问题的

MariaDB [big_data]> select count() from accountsPart;
+----------+
| count() |
+----------+
| |
+----------+
row in set (0.00 sec)

到此为止。

使用spark与MySQL进行数据交互的方法的更多相关文章

  1. SpringMVC4+thymeleaf3的一个简单实例(篇五:页面和MySql的数据交互-展示以及存储)

    这一篇将介绍怎样把页面数据保存的MySQL数据库,并将数据库内容展示到页面上.首先做一个基础工作,添加以下jar到lib:1: mysql-connector-Java-5.1.40-bin.jar ...

  2. python与mysql的数据交互

    一 Python 中操作 MySQL 步骤 1.1 安装pymysql命令 sudo pip3 install pymysql 安装软件:sudo apt-get install 软件名称 安装模块: ...

  3. mysql导入数据大小设置方法

    MySQL导入数据库文件最大限制2048KB和phpmyadmin导入数据最大限制2048KB的解决方法 解决办法: 1.打开php.ini.找到 upload_max_filesize . memo ...

  4. 使用Apache Spark 对 mysql 调优 查询速度提升10倍以上

    在这篇文章中我们将讨论如何利用 Apache Spark 来提升 MySQL 的查询性能. 介绍 在我的前一篇文章Apache Spark with MySQL 中介绍了如何利用 Apache Spa ...

  5. 通过mapreduce把mysql的数据读取到hdfs

    前面讲过了怎么通过mapreduce把mysql的一张表的数据放到另外一张表中,这次讲的是把mysql的数据读取到hdfs里面去 具体怎么搭建环境我这里就不多说了.参考 通过mapreduce把mys ...

  6. js前台与后台数据交互-前台调后台

    转自:http://blog.csdn.net/wang379275614/article/details/17033981   网站是围绕数据库来编程的,以数据库中的数据为中心,通过后台来操作这些数 ...

  7. 大数据项目实践:基于hadoop+spark+mongodb+mysql+c#开发医院临床知识库系统

    一.前言 从20世纪90年代数字化医院概念提出到至今的20多年时间,数字化医院(Digital Hospital)在国内各大医院飞速的普及推广发展,并取得骄人成绩.不但有数字化医院管理信息系统(HIS ...

  8. 基于Spark Streaming + Canal + Kafka对Mysql增量数据实时进行监测分析

    Spark Streaming可以用于实时流项目的开发,实时流项目的数据源除了可以来源于日志.文件.网络端口等,常常也有这种需求,那就是实时分析处理MySQL中的增量数据.面对这种需求当然我们可以通过 ...

  9. Spark:读取mysql数据作为DataFrame

    在日常工作中,有时候需要读取mysql的数据作为DataFrame数据源进行后期的Spark处理,Spark自带了一些方法供我们使用,读取mysql我们可以直接使用表的结构信息,而不需要自己再去定义每 ...

随机推荐

  1. Java课程设计 - 学生基本信息管理

    团队名称.团队成员介绍(需要有照片) 团队名称:此艺兴非彼艺兴 团队成员: 王兴:女,积极上进 曾艺佳:女,积极上进 项目git地址 StudentManage项目 项目git提交记录截图(要体现出每 ...

  2. 201521123095 《Java程序设计》第10周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容. 2. 书面作业 本次PTA作业题集异常.多线程 Q1.finally 题目4-2 1.1 截图你的提交结果( ...

  3. 201521123033《Java程序设计》第10周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容. answer: 2. 书面作业 本次PTA作业题集异常.多线程 1.finally 题目4-2 1.1 截图 ...

  4. 201521123026《JAVA程序设计》第11周学习总结

    1. 本章学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多线程相关内容. 1.多线程同步:限制某个资源在同一时刻只能被一个线程访问.. 2.同步代码块:`synchronized(lock ...

  5. Java第十四周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多数据库相关内容. 2. 书面作业 1. MySQL数据库基本操作 建立数据库,将自己的姓名.学号作为一条记录插入.(截图,需出现自 ...

  6. Java课程设计——学生基本信息管理

    1.团队名称.团队成员介绍 团队名称:学生基本信息管理设计小组 团队成员:花雨芸(组长)--负责管理界面的编写 丁蓉(组员)--负责登陆的设计编写 2.项目git地址 https://git.osch ...

  7. 关于Java中数组的常用操作方法

    1. 声明一个数组 String[] arr1 = new String[5]; String[] arr2 = {"a","b","c", ...

  8. mysql5.6.24的安装与简单使用

    1, 下载绿色版Mysql5.6.24 http://dlsw.baidu.com/sw-search-sp/soft/ea/12585/mysql-5.6.24-win32.1432006610.z ...

  9. java web项目修改favicon.ico图标的方式

    1.修改整个项目的tomcat图标 找到tomcat的根目录(tomcat-webapps-ROOT目录),然后将修改的favicon.ico图标覆盖掉本地的图标,然后再重启项目,刷新,清除浏览器缓存 ...

  10. mysql死锁+解决

    自己作死,navicat不恰当的操作导致了表死锁,操作如下: 给表新加字段:name 没有选择允许为空,但是有没有设置初始值,所以运行的结果就是数据库表里有了name不允许为空但是确实为空的记录: 然 ...