前面已经给大家讲过sqoop的安装,如果还没有安装的可以再去看看详细安装流程:
sqoop的作用前面也说过主要用于做数据迁移,它用于从关系数据库(如MySQL,Oracle)导入数据到Hadoop HDFS,并从Hadoop文件系统导出到关系数据库的互相导入。
Sqoop功能
- 将关系型数据库数据导入HDFS,解决HDFS的数据来源问题,使数据能够方便地进入HDFS。
- 支持HDFS数据导出到关系型数据库,Hadoop计算出的数据可以方便地写回到数据库中。
- 支持关系型数据库直接将数据导入到Hive。
- Sqoop是批处理类型的任务,不是常驻服务,不需要像WEB服务一样常驻运行,在需要的时候提交任务就可以完成数据的导入导出。
- Sqoop是使用命令行进行任务的提交,提供类似于Shell的脚本的Sqopp命令,提交方式很简便。
- Sqoop支持各种存储类型,包括行存、列存以及各种数压缩算法。
Sqoop架构
如果是你设计一个数据库数据导入导出工具的话,会怎么实现呢?首先比较简单而且也容易想到的方式是使用JDBC,将数据从数据库中拉取出来然后写入HDFS,这种方法简单易行,但是缺点也很明显,数据量较大时,效率不高。
Sqoop是怎么做的呢,它其实是依赖MapReduce的计算框架,将数据导入并行化,采用分而治之的思想,每个Map只处理一部分数据,然后由Reduce将Map的中间结果聚合起来。
其实并不需要Reduce,只是用Map就可以完成数据的并行导入导出工作了,每个Map使用JDBC将数据从数据库抽取出来,写入到HDFS,就可以完成数据的导入任务。
由于使用了MapReduce并发计算的特性,Sqoop可以显著提高数据导入导出的效率。在实际使用中,Sqoop一般不会称为性能的瓶颈,在磁盘读写和宽带都不是瓶颈的前提下,数据的导入导出效率往往取决于DB的性能。
上面的框架中Sqoop和数据库之间使用的是JDBC,所以逻辑上讲,所有支持JDBC操作的数据库都支持使用Sqoop将数据导入到HDFS中,当然各个数据库之间会存在差异,目前在不改造Sqoop的前提下,Sqoop支持的数据库有:MySQL,Oracle,SqlServer, postgreSQL,DB2等,基本涵盖了所有主流的数据库。
数据导入Hive流程
如果命令指定要新建HIVE Table,Sqoop会先生成HIVE table的定义语句,Hive table的column和数据库的column建立一一映射的关系,然后Sqoop会生成一个rawData语句,rawdata语句会在Hive MateStore中注册元数据,并进行数迁移。
上面的Hive的建表语句和rawdata语句都会被写入到一个script脚本中,最后会sqoop启动.hive命令执行刚刚生成的script脚本,提交Hive任务,完成Hive导出。
数据导入HDFS原理
Sqoop在解析完命令参数后会查找参数指定的分片字段的数据范围,假设用户指定的分片字段为id,通过jdbc接口可以找到id的最大值和最小值,分别是5000和1。
数据范围除以map数量可以得到每个Map传输的数据id范围,假设map数是5个,那么每个map的范围就是5000/5=1000。每个map使用SQL语句进行筛选,筛选方法就是在SQL中添加where条件,比如第一个map的where条件就可以写为id >= 1 and id <= 1000。
每个map都会在HDFS上生成一个对应的文件,存储向HDFS导入的数据。
数据从HDFS导出原理
数据导出跟导入正好相反,是把HDFS上的文件导出到数据库中,导出任务的数据划分完全依赖于HDFS的 FileInputFormat 的 split 划分;
默认情况下会使用 CombineInputFormat ,尽量把同一个节点的数据使用同一个map来处理,这样可以众多小文件导致的创建map任务的开销,提高任务的执行效率。
但是如果导出的数据文件是压缩格式,且这种压缩格式不能再分片,那么文件就会当以单个文件进行分片,每一个文件会生成一个map进行导出。
导出任务的分片逻辑比较复杂,用户指定的并发参数往往不会生效。
Sqoop是基于MapReduce框架的数据同步工具,启用多个map进行数据的并发导入和导出。对于导入任务,数据的分片是基于数据中数据的范围和用户指定的并发数;
对于导出任务,数据分片是依赖HDFS的 FileInputFormat 的分片策略。
Sqoop导数据语法
首先导sql文件到mysql数据库中,test.sql文件放在linux系统的opt目录下
source /opt/test.sql;
这样mysql中就有数据了。
mysql中导入按条件查询后的表到HDFS
语法:
sqoop import \
--connect jdbc:mysql://localhost:3306/数据库名 \
--driver com.mysql.jdbc.Driver \
--query ''select * from userinfos where username='zs' and \$CONDITIONS" \
--username root \
--password root \
--split-by userid \
--delete-target-dir \
--target-dir /tmp/customs \
--as-textfile
--m 3
参数解析:
- connect:指定JDBC连接数据库所在的地址、端口、数据库名称、字符编码格式等信息
- username:数据库连接用户名
- password:数据库连接密码
- query:获取导入数据的查询语句
- $CONDITIONS:query查询语句必须有的字段,\ $转义符
- split-by:指定分片字段,后续会使用改字段对数据进行换分,放到不同的map中执行,一般使用主键或者有索引的字段,可提高执行效率
- delete-target-dir:如果目标文件存在就先删除在导入
- target-dir:指定HDFS上的目标路径,即导入数据的存储路径
- as-textfile:指定导入数据到HDFS后的数据存储格式。
- m:导入的数据分成多少个文件存放
mysql中导入表的指定列到HDFS
语法:
sqoop import \
--connect jdbc:mysql://localhost:3306/数据库名 \
--driver com.mysql.jdbc.Driver \
--table test \
--username root \
--password root \
--delete-target-dir \
--target-dir /tmp/customs \
--columns "EMPNO,ENAME,JOB,SAL,COMM" \
--as-textfile
--m 3
参数解析:
table: 指定数据库的表名
columns:指定mysql数据库中的具体列导入到HDFS
mysql中增量导入数据到HDFS
语法:
sqoop import \
--connect jdbc:mysql://localhost:3306/数据库名 \
--driver com.mysql.jdbc.Driver \
--table test \
--username root \
--password root \
--Incremental append \
--check-column order_date \
--delete-target-dir \
--last_value '2013-07-25 00:00:00"
--target-dir /data/retail_db \
--m 3
参数解析:
增量导入(incremental):指定关系数据库的某个Column和last value,下次导入的时候只导入比last value大的数据。增量导入分两种模式:
- append:把新数据追加到目标路径中
- last modified:会将新数据和老数据进行合并,合并的话需要新老数据的对应关系,所以还需要添加参数merge key来指定主键,最后执行结果只保留最新数据。该模式下last value的值一定要是timestamp或者data。
- check-column: 指定递增的列
导入数据到Hive中
先在Hive中创建数据库
hive -e “create database if not exists test;”
语法:
sqoop import \
--connect jdbc:mysql://localhost:3306/数据库名 \
--driver com.mysql.jdbc.Driver \
--table test \
--username root \
--password root \
--hive-import \
--create-hive-table \
--hive-database test \
--hive-table orders \
--m 3
参数解析:
- 支持hive table的创建与覆盖:create-hive-table, hive-overwritess
- create-hive-table: 自动创建表,如果表已经存在则报错
,生产中一般不使用 - hive-overwrite:覆盖原有表数据
- hive-import: 通过hive-import指定导入到Hive
- hive-database: 指定导入的hive的数据库名
- hive-table: 指定导入的hive的表名
导入数据到Hive分区中
先删除Hive表
drop table if exists orders;