Sqoop 是传统数据库与 Hadoop 之间数据同步的工具,它是 Hadoop 发展到一定程度的必然产物,它主要解决的是传统数据库和Hadoop之间数据的迁移问题。这节课我们将详细介绍 Sqoop 这个工具。

Sqoop 产生背景

        Sqoop 的产生主要源于以下几种需求:

        1、多数使用 Hadoop 技术处理大数据业务的企业,有大量的数据存储在传统的关系型数据库(RDBMS)中。

        2、由于缺乏工具的支持,对 Hadoop 和 传统数据库系统中的数据进行相互传输是一件十分困难的事情。

        3、基于前两个方面的考虑,亟需一个在 RDBMS 与 Hadoop 之间进行数据传输的项目。

Sqoop 是什么

        Sqoop 是连接传统关系型数据库和 Hadoop 的桥梁。它包括以下两个方面:

        1、 将关系型数据库的数据导入到 Hadoop 及其相关的系统中,如 Hive和HBase。

        2、 将数据从 Hadoop 系统里抽取并导出到关系型数据库。

        Sqoop 的核心设计思想是利用 MapReduce 加快数据传输速度。也就是说 Sqoop 的导入和导出功能是通过 MapReduce 作业实现的。所以它是一种批处理方式进行数据传输,难以实现实时的数据进行导入和导出。

为什么选择 Sqoop

        我们为什么选择 Sqoop 呢?通常基于三个方面的考虑:

        1、它可以高效、可控地利用资源,可以通过调整任务数来控制任务的并发度。另外它还可以配置数据库的访问时间等等。

        2、它可以自动的完成数据类型映射与转换。我们往往导入的数据是有类型的,它可以自动根据数据库中的类型转换到 Hadoop 中,当然用户也可以自定义它们之间的映射关系。

        3、它支持多种数据库,比如,Mysql、Oracle和PostgreSQL等等数据库。

Sqoop 安装

        我们 Hadoop 集群安装的是 Hadoop2.2.0 版本,所以 Sqoop 安装版本也要与之相匹配,否则后面 Sqoop 工具的使用会出现问题。这里我们选择 sqoop-1.4.6.bin__hadoop-2.0.4-alpha.tar.gz版本安装。 安装 Sqoop 很简单,分为以下几步完成。

        1、首先将下载的 sqoop-1.4.6.bin__hadoop-2.0.4-alpha.tar.gz放到 /usr/java/目录下,然后对安装包解压、修改文件名和修改用户权限。

[root@single-hadoop-dajiangtai-com java]# tar zxvf sqoop-1.4.6.bin__hadoop-1.0.0.tar.gz //解压
[root@single-hadoop-dajiangtai-com java]# rm sqoop-1.4.6.bin__hadoop-1.0.0.tar.gz //删除安装包
[root@single-hadoop-dajiangtai-com java]# mv sqoop-1.4.6.bin__hadoop-1.0.0 sqoop //修改安装文件目录
[root@single-hadoop-dajiangtai-com java]# chown -R hadoop:hadoop sqoop //赋予sqoop hadoop用户权限

        2、切换到/sqoop/conf 目录下,执行以下命令。

[hadoop@single-hadoop-dajiangtai-com java]$ cd sqoop/conf
[hadoop@single-hadoop-dajiangtai-com java]$ mv sqoop-env-template.sh sqoop-env.sh

        然后使用 vi sqoop-env.sh 命令,打开文件添加如下内容。

#Set path to where bin/hadoop is available
export HADOOP_COMMON_HOME=/usr/java/hadoop-2.2.0-x64
#Set path to where hadoop-*-core.jar is available
export HADOOP_MAPRED_HOME=/usr/java/hadoop-2.2.0-x64
#set the path to where bin/hbase is available
#export HBASE_HOME=
#Set the path to where bin/hive is available
export HIVE_HOME=/usr/java/hive-1.0.0
#Set the path for where zookeper config dir is
#export ZOOCFGDIR=

        如果数据读取不涉及hbase和hive,那么相关hbase和hive的配置可以不加;如果集群有独立的zookeeper集群,那么配置zookeeper,反之,不用配置。

        3、将相关的驱动 jar 包拷贝到 sqoop/lib 目录下。安装 Hadoop2.2.0 的核心 jar包有三个需要导入:commons-cli-1.2.jar、hadoop-common-2.2.0.jar和hadoop-mapreduce-client-core-2.2.0.jar。 数据库驱动 jar 包需要导入,这里我们使用的是 mysql 数据库,所以需要导入mysql-connector-java-5.1.21.jar包。

[hadoop@single-hadoop-dajiangtai-com lib]$ cp commons-cli-1.2.jar /usr/java/sqoop/lib
[hadoop@single-hadoop-dajiangtai-com common]$ cp hadoop-common-2.2.0.jar /usr/java/sqoop/lib
[hadoop@single-hadoop-dajiangtai-com mapreduce]$ cp hadoop-mapreduce-client-core-2.2.0.jar /usr/java/sqoop/lib
[hadoop@single-hadoop-dajiangtai-com java]$ cp mysql-connector-java-5.1.21.jar /usr/java/sqoop/lib

        4、添加环境变量。

[hadoop@single-hadoop-dajiangtai-com java]$ vi ~/.bash_profile
PATH=$PATH:$HOME/bin
export SQOOP_HOME=/usr/java/sqoop //sqoop安装目录
export PATH=$PATH:$SQOOP_HOME/bin

        环境添加完毕后,执行以下命令使环境生效。

[hadoop@single-hadoop-dajiangtai-com java]$ source ~/.bash_profile

        5、测试运行

[hadoop@single-hadoop-dajiangtai-com java]$ sqoop list-databases \
> --connect jdbc:mysql://db.dajiangtai.net:3306/djtdb_hadoop \
> --username sqoop \
> --password sqoop
15/06/03 02:47:27 INFO sqoop.Sqoop: Running Sqoop version: 1.4.6
15/06/03 02:47:27 WARN tool.BaseSqoopTool: Setting your password on the command-line is insecure. Consider using -P instead.
15/06/03 02:47:28 INFO manager.MySQLManager: Preparing to use a MySQL streaming resultset.
information_schema
djtdb_demo
djtdb_hadoop
djtdb_www

        sqoop 命令执行成功,代表安装成功。




Sqoop 架构

        Sqoop 架构是非常简单的,它主要由三个部分组成:Sqoop client、HDFS/HBase/Hive、Database。下面我们来看一下 Sqoop 的架构图。

sqoop 架构图

        用户向 Sqoop 发起一个命令之后,这个命令会转换为一个基于 Map Task 的 MapReduce 作业。Map Task 会访问数据库的元数据信息,通过并行的 Map Task 将数据库的数据读取出来,然后导入 Hadoop 中。 当然也可以将 Hadoop 中的数据,导入传统的关系型数据库中。它的核心思想就是通过基于 Map Task (只有 map)的 MapReduce 作业,实现数据的并发拷贝和传输,这样可以大大提高效率。

Sqoop与HDFS结合

        下面我们结合 HDFS,介绍 Sqoop 从关系型数据库的导入和导出。

Sqoop import

        它的功能是将数据从关系型数据库导入 HDFS 中,其流程图如下所示。

sqoop import

        我们来分析一下 Sqoop 数据导入流程,首先用户输入一个 Sqoop import 命令,Sqoop 会从关系型数据库中获取元数据信息,比如要操作数据库表的 schema是什么样子,这个表有哪些字段,这些字段都是什么数据类型等。它获取这些信息之后,会将输入命令转化为基于 Map 的 MapReduce作业。这样 MapReduce作业中有很多 Map 任务,每个 Map 任务从数据库中读取一片数据,这样多个 Map 任务实现并发的拷贝,把整个数据快速的拷贝到 HDFS 上。

        下面我们看一下 Sqoop 如何使用命令行来导入数据的,其命令行语法如下所示。

sqoop import \
--connect jdbc:mysql://db.dajiangtai.net:3306/djtdb_hadoop \
--username sqoop \
--password sqoop \
--table user \
--target-dir /junior/sqoop/ \ //可选,不指定目录,数据默认导入到/user下
--where "sex='female'" \ //可选
--as-sequencefile \ //可选,不指定格式,数据格式默认为 Text 文本格式
--num-mappers 10 \ //可选,这个数值不宜太大
--null-string '\\N' \ //可选 
--null-non-string '\\N' \ //可选

        --connect:指定 JDBC URL。

        --username/password:mysql 数据库的用户名。

        --table:要读取的数据库表。

        --target-dir:将数据导入到指定的 HDFS 目录下,文件名称如果不指定的话,会默认数据库的表名称。

        --where:过滤从数据库中要导入的数据。

        --as-sequencefile:指定数据导入数据格式。

        --num-mappers:指定 Map 任务的并发度。

        --null-string,--null-non-string:同时使用可以将数据库中的空字段转化为'\N',因为数据库中字段为 null,会占用很大的空间。

        下面我们介绍几种 Sqoop 数据导入的特殊应用。

        1、Sqoop 每次导入数据的时候,不需要把以往的所有数据重新导入 HDFS,只需要把新增的数据导入 HDFS 即可,下面我们来看看如何导入新增数据。

sqoop import \
--connect jdbc:mysql://db.dajiangtai.net:3306/djtdb_hadoop \
--username sqoop \
--password sqoop \
--table user \
--incremental append \ //代表只导入增量数据
--check-column id \ //以主键id作为判断条件
--last-value 999 //导入id大于999的新增数据

        上述三个组合使用,可以实现数据的增量导入。

        2、Sqoop 数据导入过程中,直接输入明码存在安全隐患,我们可以通过下面两种方式规避这种风险。

        1)-P:sqoop 命令行最后使用 -P,此时提示用户输入密码,而且用户输入的密码是看不见的,起到安全保护作用。密码输入正确后,才会执行 sqoop 命令。

sqoop import \
--connect jdbc:mysql://db.dajiangtai.net:3306/djtdb_hadoop \
--username sqoop \
--table user \
-P

        2)--password-file:指定一个密码保存文件,读取密码。我们可以将这个文件设置为只有自己可读的文件,防止密码泄露。

sqoop import \
--connect jdbc:mysql://db.dajiangtai.net:3306/djtdb_hadoop \
--username sqoop \
--table user \
--password-file my-sqoop-password

Sqoop export

        它的功能是将数据从 HDFS 导入关系型数据库表中,其流程图如下所示。

sqoop export

        我们来分析一下 Sqoop 数据导出流程,首先用户输入一个 Sqoop export 命令,它会获取关系型数据库的 schema,建立 Hadoop 字段与数据库表字段的映射关系。 然后会将输入命令转化为基于 Map 的 MapReduce作业,这样 MapReduce作业中有很多 Map 任务,它们并行的从 HDFS 读取数据,并将整个数据拷贝到数据库中。

        下面我们看一下 Sqoop 如何使用命令行来导出数据的,其命令行语法如下所示。

sqoop export \
--connect jdbc:mysql://db.dajiangtai.net:3306/djtdb_hadoop \
--username sqoop \
--password sqoop \
--table user \
--export-dir user

        --connect:指定 JDBC URL。

        --username/password:mysql 数据库的用户名和密码。

        --table:要导入的数据库表。

        --export-dir:数据在 HDFS 上的存放目录。

        下面我们介绍几种 Sqoop 数据导出的特殊应用。

        1、Sqoop export 将数据导入数据库,一般情况下是一条一条导入的,这样导入的效率非常低。这时我们可以使用 Sqoop export 的批量导入提高效率,其具体语法如下。

sqoop export \
--Dsqoop.export.records.per.statement=10 \
--connect jdbc:mysql://db.dajiangtai.net:3306/djtdb_hadoop \
--username sqoop \
--password sqoop \
--table user \
--export-dir user \
--batch

        --Dsqoop.export.records.per.statement:指定每次导入10条数据,--batch:指定是批量导入。

        2、在实际应用中还存在这样一个问题,比如导入数据的时候,Map Task 执行失败, 那么该 Map 任务会转移到另外一个节点执行重新运行,这时候之前导入的数据又要重新导入一份,造成数据重复导入。 因为 Map Task 没有回滚策略,一旦运行失败,已经导入数据库中的数据就无法恢复。Sqoop export 提供了一种机制能保证原子性, 使用--staging-table 选项指定临时导入的表。Sqoop export 导出数据的时候会分为两步:第一步,将数据导入数据库中的临时表,如果导入期间 Map Task 失败,会删除临时表数据重新导入;第二步,确认所有 Map Task 任务成功后,会将临时表名称为指定的表名称。

sqoop export \
--connect jdbc:mysql://db.dajiangtai.net:3306/djtdb_hadoop \
--username sqoop \
--password sqoop \
--table user \
--staging-table staging_user

        3、在 Sqoop 导出数据过程中,如果我们想更新已有数据,可以采取以下两种方式。

        1)通过 --update-key id 更新已有数据。

sqoop export \
--connect jdbc:mysql://db.dajiangtai.net:3306/djtdb_hadoop \
--username sqoop \
--password sqoop \
--table user \
--update-key id

        2)使用 --update-key id和--update-mode allowinsert 两个选项的情况下,如果数据已经存在,则更新数据,如果数据不存在,则插入新数据记录。

sqoop export \
--connect jdbc:mysql://db.dajiangtai.net:3306/djtdb_hadoop \
--username sqoop \
--password sqoop \
--table user \
--update-key id \
--update-mode allowinsert

        4、如果 HDFS 中的数据量比较大,很多字段并不需要,我们可以使用 --columns 来指定插入某几列数据。

sqoop export \
--connect jdbc:mysql://db.dajiangtai.net:3306/djtdb_hadoop \
--username sqoop \
--password sqoop \
--table user \
--column username,sex

        5、当导入的字段数据不存在或者为null的时候,我们使用--input-null-string和--input-null-non-string 来处理。

sqoop export \
--connect jdbc:mysql://db.dajiangtai.net:3306/djtdb_hadoop \
--username sqoop \
--password sqoop \
--table user \
--input-null-string '\\N' \
--input-null-non-string '\\N'

Sqoop与其它系统结合

        Sqoop 也可以与Hive、HBase等系统结合,实现数据的导入和导出,用户需要在 sqoop-env.sh 中添加HBASE_HOME、HIVE_HOME等环境变量。

        1、Sqoop与Hive结合比较简单,使用 --hive-import 选项就可以实现。

sqoop import \
--connect jdbc:mysql://db.dajiangtai.net:3306/djtdb_hadoop \
--username sqoop \
--password sqoop \
--table user \
--hive-import

        2、Sqoop与HBase结合稍微麻烦一些,需要使用 --hbase-table 指定表名称,使用 --column-family 指定列名称。

sqoop import \
--connect jdbc:mysql://db.dajiangtai.net:3306/djtdb_hadoop \
--username sqoop \
--password sqoop \
--table user \
--hbase-table user \
--column-family city