Sqoop是通过MapReduce作业进行导入工作,在作业中,会从表中读取一行行记录,然后将其写入HDFS
在开始导入之前,Sqoop会通过JDBC来获得所需要的数据库元数据,例如,导入表的列名,数据类型等(第一步);接着这些数据库的数据类型(varchar,number等)会被映射成java类型(String,int等),根据这些信息,Sqoop会生成一个与表名同名的类用来完成反序列化的工作,保存表中的每一行记录(第二步);Sqoop启动MapReduce作用(第三步),MapReduce 中主要是对 InputFormat 和 OutputFormat 进行定制;启动的作业在input的过程中,会通过JDBC读取数据库表中的内容(第四步),这时,会使用Sqoop生成的类进行反序列化;最后再将这些记录写到HDFS中,在写入HDFS的过程中,同样会使用Sqoop生成的类进行序列化。
Sqoop的导入作业通常不只是由一个Map任务完成,也就是说每个任务会获取表的一部分数据。如果只由一个Map任务完成导入的话,那么在第四步时,作业会通过JDBC执行如下SQL:
SELECT col1,col2,...FROM table ;
这样就能获得表的全部数据,如果需要多个Map任务来完成,那就必须对表进行水平切分,水平切分的依据通常会是表的主键。Sqoop在启动MapReduce作业时,会首先通过JDBC查询切分类的最大值和最小值,再根据启动的任务数(使用命令 -m指定)划分出每个任务所负责的数据,实际在第四步时,每个任务执行的SQL为:
SELECT col1,col2,...FROM table WHERE id >=0 AND < 50000;
SELECT col1,col2,...FROM table WHERE id >=50000 AND < 100000;
........
使用Sqoop进行并行导入的话,切分列的数据会很大程度地影响性能,如果在均匀分布的情况下,性能最好。在最坏的情况下,数据严重倾斜,所有数据都集中在某一个切分区中,那么此时的性能与串行导入没有差别,所以,在导入之前,有必要对切分列的数据进行抽样检测,了解数据的分布。
Sqoop可以对导入过程进行精细地控制,不用每次都导入一张表的所有字段。Sqoop允许我们知道表的列,在查询中加入WHERE自居,甚至可以自定义查询SQL语句,并且在SQL语句中,可以任意使用目标数据库所支持的函数。
一旦数据导出完成后,这份数据就可被MapReduce程序所使用。但是我们知道,对于结构化的数据来说,Hive才是最适合处理的,所以自然而然也提供了相应的功能。
将导入的数据存放到了HDFS中,将这份数据导入Hive之前,必须在Hive中创建该表,Sqoop提供了相应的命令:
sqoop create-hive-table --connect jdbc: mysql : //master:3306/hive --table DBS
--fields-terminated-by ',' --username root
这时登录Hive会发现DBS表已经创建好了,但是没有数据。这时只需执行:
hive>LOAD DATA INPATH '/user/hadoop/DBS/part-r-00000' INTO TABLE DBS;
这里要注意,由于Sqoop默认导出格式为逗号分隔,索引在Sqoop建表命令中,我们用 --fields-terminated-by ',' 指明Hive中DBS表的列分隔符。
如果想直接从数据库将数据导入Hive中,也就是将上述3个步骤(导入HDFS,创建表,加载)合并为一个步骤,Sqoop也提供了相应的命令:
Sqoop import --connect jdbc:mysql://master:3306/hive --table DBS --username root -m 1 --hive-import
通过加上 --hive-import选项,Sqoop可以根据源数据库中的表结构来自动生成Hive表的结构,这样Sqoop就可方便地将数据直接导入Hive中。
与Sqoop导入功能相比,Sqoop的导出功能使用频率相对较低,一般都是将Hive的分析结果导出到关系型数据库以供数据分析师查看,生成报表等。
在将Hive中的表导出到数据库时,必须在数据库中新建一张用来接收数据的表,需要导出的Hive表为 test_table,如下:
hive > DESC test_table ;
OK
id string
Time taken:0.109 seconds
我们在mysql中新建一张用于接收数据的表,如下:
mysql>use test;
Database changed
mysql>create table test_received(id varchar(5));
这里需要注意的是,在Hive中,字符串数据类型为STRING,但是在关系型数据库中,有可能是varchar(20),varchar(100),z这些必须根据情况自己指定,这也是必须由用户事先将表创建好的原因。接下来,就可以执行导入了,执行命令:
sqoop export --connect jdbc:mysql://master:3306/test --table test_table --export-dir
/user/hive/warehouse/test_table --username root -m 1 --fields-terminated-by '\t'
导出完毕后,就可以在Mysql中通过表test_received进行查询。对于上面这条导出命令,另外我们还需要将Hive表的列分隔符通过 --fields-terminated-by告知sqoop。
在了解导入过程后,导出过程就变得容易理解了
同样的,sqoop根据目标表的结构会生成一个java类(第一步和第二步),该类的作用为序列化和反序列化。接着会启动一个MapReduce作业(第三步),在作业中会用生成的java类从HDFS中读取数据(第四步),并生成一批INSERT语句,每条语句都会向MySQL的目标表中插入多条记录(第五步),这样读入的时候是并行,写入的时候也是并行,但是其写入性能会受限于数据库的写入性能。