Sqoop介绍
sqoop是apache旗下一款“Hadoop和关系数据库服务器之间传送数据”的工具。 导入数据:MySQL,Oracle导入数据到Hadoop的HDFS、HIVE、HBASE等数据存储系统; 导出数据:从Hadoop的HDFS、HIVE中导出数据到关系数据库mysql等。
Sqoop工作机制
Sqoop工作机制是将导入或导出命令翻译成mapreduce程序来实现。 在翻译出的mapreduce中主要是对inputformat和outputformat进行定制。
sqoop安装
你的hive安装在哪一台主机,你的sqoop就要装在哪一台主机,该操作是以node1为例 1、在node1中,上传sqoop的安装包到/export/software目录 2、在node1中,解压sqoop安装包到/export/server目录 tar -xvf sqoop-1.4.7.bin__hadoop-2.6.0.tar.gz -C /export/server/ 3、在node1中,对解压后的sqoop目录进行重命名 cd /export/server/ mv sqoop-1.4.7.bin__hadoop-2.6.0 sqoop-1.4.7 4、在node1中,修改sqoop的配置文件, cd /export/server/sqoop-1.4.7/conf mv sqoop-env-template.sh sqoop-env.sh 修改sqoop-env.sh 文件,设置以下内容 export HADOOP_COMMON_HOME=/export/server/hadoop-3.3.0 export HADOOP_MAPRED_HOME=/export/server/hadoop-3.3.0 export HIVE_HOME=/export/server/hive-3.1.2 5、在node1中,加入mysql的jdbc驱动包和hive的执行包 cp /export/server/hive-3.1.2/lib/mysql-connector-java-5.1.32-bin.jar /export/server/sqoop-1.4.7/lib/ cp /export/servers/hive-3.1.2/lib/hive-exec-3.1.2.jar /export/servers/sqoop-1.4.7/lib/ cp /export/server/hive-3.1.2/hcatalog/share/hcatalog/hive-hcatalog-core-3.1.2.jar /export/server/sqoop-1.4.7/lib/ 6、在node1,node2、node3中,配置环境变量 vim /etc/profile 添加以下内容 export SQOOP_HOME=/export/server/sqoop-1.4.7 export PATH=:$SQOOP_HOME/bin:$PATH # HCatelog export HCAT_HOME=/export/server/hive-3.1.2/hcatalog export hive_dependency=$HIVE_HOME/conf:$HIVE_HOME/lib/*:$HIVE_HOME/hcatalog/share/hcatalog/hive-hcatalog-core-3.1.2.jar 添加完之后一定要保存退出,执行以下命令 source /etc/profile 7、在node1中,测试sqoop sqoop list-databases \ --connect jdbc:mysql://node1:3306/ \ --username root --password 123456
sqoop测试
#测试你的sqoop是否能查看MySQL中所有的数据库 sqoop list-databases \ --connect jdbc:mysql://hadoop01:3306/ \ --username root \ --password 123456
增量数据、全量数据
全量数据(Full data)
就是全部数据,所有数据。如对于表来说,就是表中的所有数据。
增量数据(Incremental data)
就是上次操作之后至今产生的新数据。
数据子集
也叫做部分数据。整体当中的一部分。
Sqoop数据导入至HDFS
测试数据准备
全量导入MySQL数据到HDFS
sqoop import \ --connect jdbc:mysql://hadoop01:3306/userdb \ --table emp \ --username root \ --password 123456 \ --target-dir /sqoop/result1 \ --delete-target-dir \ --m 1 # --table emp 要导入mysql中的userdb数据库的emp表数据 # --target-dir /sqoop/result1 表示要导入到hdfs的目标路径 # --delete-target-dir 如果目标目录存在,则删除 # --m 1 使用一个Map线程来导入 导入HDFS之后,默认分隔符是逗号
指定分隔符
sqoop import \ --connect jdbc:mysql://192.168.88.80:3306/userdb \ --username root \ --password 123456 \ --target-dir /sqoop/result2 \ --delete-target-dir \ --fields-terminated-by '\t' \ --table emp \ --m 1 # --fields-terminated-by '\t' 指定HDFS上文件的分隔符是'\t'
指定任务并行度(maptask个数)
sqoop import \ --connect jdbc:mysql://192.168.88.80:3306/userdb \ --username root \ --password 123456 \ --target-dir /sqoop/result3 \ --delete-target-dir \ --fields-terminated-by '\t' \ --split-by id \ --table emp \ --m 2 #如果你要指定多个maptask来完成数据的导入,也就是--m参数的值不是1,则必须添加一个参数--split-by #该参数用来指定你原表的数据如何分配给多个线程来实现导入 #--split-by id 内部原理是获取id的最小值和id的最大值,进行平均划分 SELECT MIN(`id`), MAX(`id`) FROM `emp` #maptask1要导入的数据 1201 gopal manager 50000 TP 1202 manisha Proof reader 50000 TP #maptask2要导入的数据 1203 khalil php dev 30000 AC 1204 prasanth php dev 30000 AC 1205 kranthi admin 20000 TP sqoop import \ -Dorg.apache.sqoop.splitter.allow_text_splitter=true \ --connect jdbc:mysql://192.168.88.80:3306/userdb \ --username root \ --password 123456 \ --target-dir /sqoop/result3 \ --delete-target-dir \ --fields-terminated-by '\t' \ --split-by name \ --table emp \ --m 2 #小结:split-by后边的字段必须是数字类型,或者是数字字符串类型(‘123’) 如果是数字类型,则不需要额外添加内容 如果是数字字符串类型,则需要添加以下内容 sqoop import \ -Dorg.apache.sqoop.splitter.allow_text_splitter=true \ --connect jdbc:mysql://192.168.88.80:3306/userdb \ --username root \ --password 123456 \ --target-dir /sqoop/result3 \ --delete-target-dir \ --fields-terminated-by '\t' \ --split-by name \ --table emp \ --m 2
Sqoop数据导入至Hive
测试准备
-- Hive中创建测试使用的数据库 drop database if exists test cascade ; create database if not exists test;
方式1-先复制mysql的表结构到Hive,然后再导入数据
1、先复制表结构到hive中再导入数据,将关系型数据的表结构复制到hive中 sqoop create-hive-table \ --connect jdbc:mysql://hadoop01:3306/userdb \ --table emp_add \ --username root \ --password 123456 \ --hive-table test.emp_add_sp 其中: --table emp_add为mysql中的数据库userdb中的表。 --hive-table emp_add_sp 为hive中新建的表名称。 复制表结构默认分隔符是'\001' 2、从关系数据库导入文件到hive中 sqoop import \ --connect jdbc:mysql://hadoop01:3306/userdb \ --username root \ --password 123456 \ --table emp_add \ --hive-table test.emp_add_sp \ --hive-import \ --m 1
方式2:直接导入数据(建表 + 导入数据)
-- 1、使用hive默认分隔符 '\001' sqoop import \ --connect jdbc:mysql://192.168.88.80:3306/userdb \ --table emp_conn \ --username root \ --password 123456 \ --hive-import \ --hive-database test \ --m 1 #从MySQL的userdb数据库的emp_conn表导入到hive的test数据库的emp_conn表 #如果多次导入,则会进行数据追加 #如果要覆盖操作,需要加参数: --hive-overwrite -- 2、使用指定分隔符 '\0' sqoop import \ --connect jdbc:mysql://192.168.88.80:3306/userdb \ --username root \ --password 123456 \ --table emp_conn \ --hive-import \ --hive-database test \ --fields-terminated-by '\t' \ --m 1 帮助手册: sqoop help sqoop help import
Sqoop数据导入至Hive--HCatalog API
sqoop API 原生方式
所谓sqoop原生的方式指的是sqoop自带的参数完成的数据导入。
但是有什么不好的地方呢?请看下面案例
-- 手动在hive中建一张表 create table test.emp_hive ( id int, name string, deg string, salary int, dept string ) row format delimited fields terminated by '\t' stored as orc; --注意,这里指定了表的文件存储格式为ORC。 --从存储效率来说,ORC格式胜于默认的textfile格式。
sqoop import \ --connect jdbc:mysql://hadoop01:3306/userdb \ --username root \ --password 123456 \ --table emp \ --fields-terminated-by '\t' \ --hive-database test \ --hive-table emp_hive \ -m 1
执行之后,可以发现虽然针对表emp_hive的sqoop任务成功,但是==Hive表中却没有数据==。
==HCatalog== API方式
Apache HCatalog是基于Apache Hadoop之上的数据表和存储管理服务。
包括:
提供一个共享的模式和数据类型的机制。
抽象出表,使用户不必关心他们的数据怎么存储,底层什么格式。
提供可操作的跨数据处理工具,如Pig,MapReduce,Streaming,和Hive。
sqoop的官网也做了相关的描述说明,使用HCatalog支持ORC等数据格式。
sqoop import \ --connect jdbc:mysql://192.168.88.80:3306/userdb \ --username root \ --password 123456 \ --table emp \ --fields-terminated-by '\t' \ --hcatalog-database test \ --hcatalog-table emp_hive \ -m 1
可以发现数据导入成功,并且底层是使用ORC格式存储的。
sqoop原生API和 HCatalog区别
#数据格式支持(这是实际中使用HCatalog的主要原因,否则还是原生的灵活一些) Sqoop方式支持的数据格式较少; HCatalog支持的数据格式多,包括RCFile, ORCFile, CSV, JSON和SequenceFile等格式。 #数据覆盖 Sqoop方式允许数据覆盖,HCatalog不允许数据覆盖,每次都只是追加。 #字段名匹配 Sqoop方式比较随意,不要求源表和目标表字段相同(字段名称和个数都可以不相同),它抽取的方式是将字段按顺序插入,比如目标表有3个字段,源表有一个字段,它会将数据插入到Hive表的第一个字段,其余字段为NULL。 但是HCatalog不同,源表和目标表字段名需要相同,字段个数可以不相等,如果字段名不同,抽取数据的时候会报NullPointerException错误。HCatalog抽取数据时,会将字段对应到相同字段名的字段上,哪怕字段个数不相等。
行存储和列存储
1、行存储 1)表数据在硬盘上以行为单位,一行的数据是连续存储在一起 ,select * from A 查询效率高。 2)行存储代表:TextFile、SequenceFile create table test.emp_hive ( id int, name string, deg string, salary int, dept string ) row format delimited fields terminated by '\t' stored as textfile; 2、列存储 1)表数据在硬盘上以列为单位,一列的数据是连续存储在一起 ,select 字段 from A 查询效率高。 2)列存储代表:ORC、Parquet create table test.emp_hive ( id int, name string, deg string, salary int, dept string ) row format delimited fields terminated by '\t' stored as orc; 3、ORC格式插入数据的步骤 1)准备数据:log.dat 18.2M 2)创建普通表,存储格式是TEXTFILE create temporary table log_text ( track_time string, url string, session_id string, referer string, ip string, end_user_id string, city_id string ) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' STORED AS TEXTFILE ; 3)给普通表插入数据 load data local inpath '/root/log.data' into table log_text; 4)创建ORC存储的表 create table log_orc( track_time string, url string, session_id string, referer string, ip string, end_user_id string, city_id string ) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' STORED AS orc ; 5)从普通表查询数据插入到orc表 insert into table log_orc select * from log_text;
知识点07:Sqoop数据导入--条件部分导入
在实际开发中,有时候我们从RDBMS(MySQL)中导入数据时,不需要将数据全部导入,而只需要导入满足条件的数据,则需要进行条件导入
query查询
sqoop import \ --connect jdbc:mysql://192.168.88.80:3306/userdb \ --username root \ --password 123456 \ --target-dir /sqoop/result5 \ --query 'select id,name,deg from emp WHERE id>1203 and $CONDITIONS' \ --delete-target-dir \ --fields-terminated-by '\001' \ --m 1
使用sql query语句来进行查找时,==不能加参数--table==;
并且==必须要添加where条件==;
并且where条件后面==必须带一个$CONDITIONS==这个字符串;
并且这个sql语句==必须用单引号==,不能用双引号。
Sqoop数据导入--增量导入
1、全量导入,表所有数据全部导入 2、条件导入,只要满足查询条件的就导入 3、增量导入,之前已经导入过一次,下一次只导入新增加或者修改的数据 方式1-使用sqoop自带的参数实现增量导入 方式2-使用用户自定义条件来实现增量导入(使用该方式比较多) 增量导入的难点: 因为你之前已经导入多一次,下一次导入时一定要判断哪些数据是已经导入过的,则不要导入,哪些数据是新增加的或者修改的,则需要导入
方式一:sqoop自带参数实现
设计思路:对某一列值进行判断,只要大于上一次的值就会导入。
所谓的增量实现,肯定需要一个判断的依据,上次到哪里了,这次从哪里开始。
--check-columnSource column to check for incremental change --incremental Define an incremental import of type 'append' or 'lastmodified' --last-value Last imported value in the incremental check column
==append==模式
要求:必须有一列自增的值,按照==自增的int值==进行判断
特点:只能导入增加的数据,无法导入更新的数据
#第一次,全量导入-首先执行以下指令先将我们之前的数据导入 sqoop import \ --connect jdbc:mysql://192.168.88.80:3306/userdb \ --username root \ --password 123456 \ --target-dir /sqoop/appendresult \ --table emp \ --m 1 #查看生成的数据文件,发现数据已经导入到hdfs中. #模拟新增加数据,然后在mysql的emp中插入2条数据: insert into `userdb`.`emp` (`id`, `name`, `deg`, `salary`, `dept`) values ('1206', 'allen', 'admin', '30000', 'tp'); insert into `userdb`.`emp` (`id`, `name`, `deg`, `salary`, `dept`) values ('1207', 'woon', 'admin', '40000', 'tp'); #第二次导入,执行如下的指令,实现增量的导入: sqoop import \ --connect jdbc:mysql://192.168.88.80:3306/userdb \ --username root \ --password 123456 \ --table emp --m 1 \ --target-dir /sqoop/appendresult \ --incremental append \ --check-column id \ --last-value 1205 ####如果想实现sqoop自动维护增量记录 可以使用sqoop job作业来实现 21/10/09 15:03:37 INFO tool.ImportTool: --incremental append 21/10/09 15:03:37 INFO tool.ImportTool: --check-column id 21/10/09 15:03:37 INFO tool.ImportTool: --last-value 1207 21/10/09 15:03:37 INFO tool.ImportTool: (Consider saving this with 'sqoop job --create')
并且还可以结合sqoop job作业,实现sqoop自动记录维护last-value值,详细可以参考课程资料。
lastmodifield模式
要求:==必须包含动态时间变化这一列==,按照数据变化的时间进行判断
特点:既导入新增的数据也导入更新的数据
# 首先我们要在mysql中创建一个customer表,指定一个时间戳字段 create table userdb.customertest( id int,name varchar(20), last_mod timestamp default current_timestamp on update current_timestamp ); #此处的时间戳设置为在数据的产生和更新时都会发生改变. #插入如下记录: insert into userdb.customertest(id,name) values(1,'neil'); insert into userdb.customertest(id,name) values(2,'jack'); insert into userdb.customertest(id,name) values(3,'martin'); insert into userdb.customertest(id,name) values(4,'tony'); insert into userdb.customertest(id,name) values(5,'eric'); #第一次全量导入:此时执行sqoop指令将数据导入hdfs: sqoop import \ --connect jdbc:mysql://192.168.88.80:3306/userdb \ --username root \ --password 123456 \ --target-dir /sqoop/lastmodifiedresult \ --table customertest --m 1 #再次插入一条数据进入customertest表 insert into userdb.customertest(id,name) values(6,'james'); #更新一条已有的数据,这条数据的时间戳会更新为我们更新数据时的系统时间. update userdb.customertest set name = 'NEIL' where id = 1; #第二次导入:执行如下指令,把id字段作为merge-key: sqoop import \ --connect jdbc:mysql://192.168.88.80:3306/userdb \ --username root \ --password 123456 \ --table customertest \ --target-dir /sqoop/lastmodifiedresult \ --incremental lastmodified \ --check-column last_mod \ --last-value '2022-10-08 15:40:27' \ --m 1 \ --merge-key id #解释: --incremental lastmodified 第二次导入数据和第一次导入的数据合并,重新执行MR,将MR执行的 覆盖原来的数据 --last-value "2022-06-10 10:59:36" 把大于等于这个日期的数据导入 --merge-key id 如果第一次导入的数据和第二次导入的数据id相同,则合并 #由于merge-key这种模式是进行了一次完整的mapreduce操作, #因此最终我们在lastmodifiedresult文件夹下可以发现id=1的name已经得到修改,同时新增了id=6的数据
方式二:用户条件过滤实现(不用敲)
通过where对字段进行过滤
-- 导入从某一个时间点的数据 sqoop import \ --connect jdbc:mysql://192.168.88.80:3306/userdb \ --username root \ --password 123456 \ --query "select * from customertest where last_mod >'2022-06-10 10:59:36' and \$CONDITIONS" \ --fields-terminated-by '\001' \ --hcatalog-database test \ --hcatalog-table customertest \ -m 1 -- 导入上一天数据,指定时间区间 sqoop import \ --connect jdbc:mysql://192.168.88.80:3306/userdb \ --username root \ --password 123456 \ --query "select * from customertest where last_mod >= '2022-09-12 00:00:00' and last_mod <= '2022-09-12 23:59:59' and \$CONDITIONS" \ --fields-terminated-by '\001' \ --hcatalog-database test \ --hcatalog-table customertest \ -m 1 sqoop import \ --connect jdbc:mysql://192.168.88.80:3306/userdb \ --username root \ --password 123456 \ --query "select * from emp where id>1203 and \$CONDITIONS" \ --fields-terminated-by '\001' \ --hcatalog-database test \ --hcatalog-table emp_hive \ -m 1
Sqoop数据导出
sqoop导出操作最大的特点是,==目标表需要自己手动提前创建==。
1、在大数据中,数据的导出一般发生在大数据分析的最后一个阶段 2、将分析后的指标导入一些通用的数据库系统中,用于进一步使用 3、导入到MySQL时,需要在MySQL中提前创建表
全量数据导出
#step1:MySQL中建表 mysql> use userdb; mysql> create table employee ( id int primary key, name varchar(20), deg varchar(20), salary int, dept varchar(10)); #step2:从HDFS导出数据到MySQL sqoop export \ --connect jdbc:mysql://192.168.88.80:3306/userdb \ --username root \ --password 123456 \ --table employee \ --export-dir /sqoop/result1/ #解释:将HDFS/sqoop/result1/目录下的文件内容,导出到mysql中userdb数据库下的employee表 #step3:从Hive导出数据到MySQL #首先清空MySQL表数据 truncate table employee; sqoop export \ --connect "jdbc:mysql://192.168.88.80:3306/userdb? useUnicode=true&characterEncoding=utf-8" \ --username root \ --password 123456 \ --table employee \ --hcatalog-database test \ --hcatalog-table emp_hive \ --input-fields-terminated-by '\t' \ -m 1 #解释: 将Hive中test数据库下的emp_hive表导出到MySQL的userdb数据库中employee表 #--input-fields-terminated-by '\t' :表示sqoop去hive的表目录下读取文件时,使用'\t'对文件进行切割,如果hive文件分隔符是'\001',则该参数不用指定 #注意,如果Hive中的表底层是使用ORC格式存储的,那么必须使用hcatalog API进行操作。
增量数据导出
updateonly:只增量导出更新的数据
allowerinsert:既导出更新的数据,也导出新增的数据
updateonly模式
#在HDFS文件系统中/sqoop/updateonly/目录的下创建一个文件updateonly_1.txt hadoop fs -mkdir -p /sqoop/updateonly/ hadoop fs -put updateonly_1.txt /sqoop/updateonly/ 1201,gopal,manager,50000 1202,manisha,preader,50000 1203,kalil,php dev,30000 #手动创建mysql中的目标表 CREATE TABLE userdb.updateonly ( id INT NOT NULL PRIMARY KEY, name VARCHAR(20), deg VARCHAR(20), salary INT ); #先执行全部导出操作: sqoop export \ --connect jdbc:mysql://192.168.88.80:3306/userdb \ --username root \ --password 123456 \ --table updateonly \ --export-dir /sqoop/updateonly/updateonly_1.txt #新增一个文件updateonly_2.txt:修改了前三条数据并且新增了一条记录 1201,gopal,manager,1212 1202,manisha,preader,1313 1203,kalil,php dev,1414 1204,allen,java,1515 hadoop fs -put updateonly_2.txt /sqoop/updateonly/ #执行更新导出: sqoop export \ --connect jdbc:mysql://192.168.88.80:3306/userdb \ --username root \ --password 123456 \ --table updateonly \ --export-dir /sqoop/updateonly/updateonly_2.txt \ --update-key id \ --update-mode updateonly #解释: --update-key id 根据id这列来判断id两次导出的数据是否是同一条数据,如果是则更新 --update-mode updateonly 导出时,只导出第一次和第二次的id都有的数据,进行更新,不会导出HDFS中新增加的数据
allowinsert模式
#手动创建mysql中的目标表 CREATE TABLE userdb.allowinsert ( id INT NOT NULL PRIMARY KEY, name VARCHAR(20), deg VARCHAR(20), salary INT); #先执行全部导出操作 sqoop export \ --connect jdbc:mysql://192.168.88.80:3306/userdb \ --username root \ --password 123456 \ --table allowinsert \ --export-dir /sqoop/updateonly/updateonly_1.txt #执行更新导出 sqoop export \ --connect jdbc:mysql://192.168.88.80:3306/userdb \ --username root --password 123456 \ --table allowinsert \ --export-dir /sqoop/updateonly/updateonly_2.txt \ --update-key id \ --update-mode allowinsert
# 博学谷IT 技术支持