一、介绍
该export工具将一组文件从HDFS导入RDBMS。目标表必须已经存在于数据库中。根据用户指定的分隔符读取输入文件并将其解析为一组记录。
1、模式
sqoop-export有三种模式:
默认模式:将它们转换为一组将INSERT语句注入数据库的语句。
更新模式:Sqoop将生成UPDATE替换数据库中现有记录的语句。
调用模式:Sqoop将为每条记录创建一个存储过程调用。
2、语法
和之前的导入工具一样,sqoop-export也有两种语法:
sqoop export (generic-args) (export-args) sqoop-export (generic-args) (export-args)
二、参数
1、常用参数
--connect <jdbc-uri>:指定JDBC连接的数据库地址。 --connection-manager <class-name>:指定要使用的连接管理器类。 --driver <class-name>:手动指定要使用的JDBC驱动类。 --hadoop-mapred-home <dir>:指定$ HADOOP_MAPRED_HOME路径 --help:打印使用说明 --password-file:为包含认证密码的文件设置路径。 -P:从控制台读取密码。 --password <password>:设置验证密码。 --username <username>:设置验证用户名。 --verbose:在工作时打印更多信息。 --connection-param-file <filename>:提供连接参数的可选属性文件。 --relaxed-isolation:将连接事务隔离设置为未提交给映射器的读取。
2、验证参数
--validate:启用对复制数据的验证,仅支持单个表复制。 --validator <class-name>:指定要使用的验证程序类。 --validation-threshold <class-name>:指定要使用的验证阈值类。 --validation-failurehandler <class-name>:指定要使用的验证失败处理程序类。
3、导出控制参数
1.参数
--columns <col,col,col…>:要导出到表格的列。 --direct:使用直接导出快速路径。 --export-dir <dir>:用于导出的HDFS源路径。 -m,--num-mappers <n>:使用n个mapper任务并行导出。 --table <table-name>:要填充的表。 --call <stored-proc-name>:存储过程调用。 --update-key <col-name>:锚点列用于更新。如果有多个列,请使用以逗号分隔的列列表。 --update-mode <mode>:指定在数据库中使用不匹配的键找到新行时如何执行更新。mode包含的updateonly默认值(默认)和allowinsert。 --input-null-string <null-string>:字符串列被解释为空的字符串。 --input-null-non-string <null-string>:要对非字符串列解释为空的字符串。 --staging-table <staging-table-name>:数据在插入目标表之前将在其中展开的表格。 --clear-staging-table:表示可以删除登台表中的任何数据。 --batch:使用批处理模式执行基础语句。
2.详解
一条导出语句中,必须有--export-dir参数和一个--table或者--call参数。这些指定要填充到数据库(或要调用的存储过程)的表以及HDFS中包含源数据的目录。
1>选择列
--columns参数选择列并控制它们的排序。
默认情况下,表格中的所有列都被选中用于导出。以逗号为间隔选择和排列各个列。
例如:
--columns "col1,col2,col3"
注意:--columns参数中不包含的列需要定义默认值或允许NULL值。否则,数据库将拒绝导入数据,从而导致sqoop导入失败。
2>先择并行
--num-mappers或-m 参数控制mapper任务的数量。默认情况下,Sqoop将为导出过程提供4个并行任务。
也可以根据目录中存在的文件数来控制mapper的数量。导出性能取决于并行度。
如果数据库已经在更新索引,调用触发器等方面遇到瓶颈,则额外的负载可能会降低性能。
3>dircet模式
--direct参数来指定direct模式的代码路径。此项可能比标准JDBC的性能更高。
4>字符串转换
--input-null-string和--input-null-non-string参数都是可选的。如果--input-null-string未指定,那么对于字符串类型的列,字符串“null”将被解释为空。如果--input-null-non-string未指定,则字符串“null”和空字符串将被解释为非字符串列的空值。
注意,除了由--input-null-non-string参数指定外,空字符串将始终被解释为非字符串列的空值。
5>指定分段表
--staging-table选项充当用于分阶段导出数据的辅助表。
由于Sqoop将导出过程分解为多个事务,导致失败的导出作业可能导致部分数据被提交给数据库。这可能进一步导致后续作业由于在某些情况下插入冲突而失败,或导致其他数据中的重复数据。那么这种情况下就可以通过指定临时表来解决此问题,该阶段性数据最终在单个事务中移动到目标表中。
为了使用分段工具,您必须在运行导出作业之前创建分段表。该表必须在结构上与目标表相同。此表应该在导出作业运行之前为空,或者--clear-staging-table必须指定该选项。如果临时表包含数据并且指定了--clear-staging-table选项,则Sqoop将在开始导出作业之前删除分段表中所有数据。
注意:
在将数据导入目标表之前支持暂存数据,但是不可用于--direct导出。--update-key更新现有数据的选项以及存储过程用于插入数据时调用导出时也不可用。
4、输入格式参数
--input-enclosed-by <char>:设置必需的字段封闭器。 --input-escaped-by <char>:设置输入转义字符。 --input-fields-terminated-by <char>:设置输入字段分隔符。 --input-lines-terminated-by <char>:设置输入的行尾字符。 --input-optionally-enclosed-by <char> 设置字段包含字符。
5、输出格式参数
--enclosed-by <char>:设置必需的字段包围字符。 --escaped-by <char>:设置转义字符。 --fields-terminated-by <char>:设置字段分隔符。 --lines-terminated-by <char>:设置行尾字符。 --mysql-delimiters:使用MySQL的默认分隔符集:fields:, lines:\n escaped-by:\ optional-enclosed-by:'。 --optionally-enclosed-by <char>:设置字段包含字符。
Sqoop会自动生成代码来解析和解释包含要导出到数据库的数据的文件记录。如果这些文件是使用非默认分隔符(以换行符分隔的记录的逗号分隔字段)创建的,则应该再次指定相同的分隔符,以便Sqoop可以解析您的文件。
如果指定了不正确的分隔符,则Sqoop将无法在每行中找到足够的列。这会导致导出mapper任务失败并抛出异常:ParseExceptions。
6、代码生成参数
--bindir <dir>:编译对象的输出目录。 --class-name <name>:设置生成的类名称。这覆盖--package-name。与之结合使用时--jar-file,设置输入类。 --jar-file <file>:禁用代码生成;使用指定的jar。 --outdir <dir>:生成代码的输出目录。 --package-name <name>:将自动生成的类放入此包中。 --map-column-java <m>:覆盖已配置列的从SQL类型到Java类型的默认映射。
如果要导出的记录是作为先前导入的结果生成的,则可以使用原始生成的类读取数据。在这种情况下指定--jar-file和--class-name避免指定分隔符。
现有生成的代码的使用与--update-key是不兼容的;更新模式导出需要新的代码生成来执行更新。也不能使用--jar-file参数,并且必须完全指定任何非默认分隔符。
三、插入与更新
1、插入
默认情况下,sqoop-export将新行添加到表中;每行输入记录都被转换成一条INSERT语句,将此行记录添加到目标数据库表中。如果数据库中的表具有约束条件(例如,其值必须唯一的主键列)并且已有数据存在,则必须注意避免插入违反这些约束条件的记录。如果INSERT语句失败,导出过程将失败。此模式主要用于将记录导出到可以接收这些结果的空表中。
2、更新
如果指定了--update-key参数,则Sqoop将改为修改数据库中表中现有的数据。每个输入记录都将转化为UPDATE语句修改现有数据。语句修改的行取决于--update-key指定的列名,如果数据库中的表中不存在的数据,那么也不会插入。
例如,请考虑下表定义:
CREATE TABLE foo( id INT NOT NULL PRIMARY KEY, msg VARCHAR(32), bar INT);
还要考虑HDFS中包含如下记录的数据集:
0,this is a test,42 1,some more data,100 ...
运行sqoop-export --table foo --update-key id --export-dir /path/to/data --connect …将运行一个导出作业,根据数据执行SQL语句,如下所示:
UPDATE foo SET msg='this is a test', bar=42 WHERE id=0; UPDATE foo SET msg='some more data', bar=100 WHERE id=1; ...
如果UPDATE语句不修改任何行不会被视为错误;导出将继续。(实际上,这意味着基于更新的导出不会将新行插入到数据库中。)同样,如果--update-key指定的列没有唯一标识行并且多行由单个语句更新,则此条件也检测不到。
--update-key参数也可以用逗号分隔列名称。在这种情况下,Sqoop在更新现有记录之前将匹配参数列表中的所有列。
3、更新or插入
根据目标数据库的不同,如果要更新数据库中已存在的数据行,或者如果行尚未存在,也可以插入行,可以使用--update-mode参数指定allowinsert模式。
这个模式下,--update-key指定的字段在数据库表中必须是唯一非空的(简单理解主键就行),这样此模式才能实现数据库表中已存在的数据进行更新,不存在的数据进行插入。否则这个模式会将所有数据都以insert语句插入数据库中。(这是我踩过的一个坑)。
例如:
在MySQL数据库中创建一个user_test表,语句如下:
CREATE TABLE `user_test` ( `id` int(11) DEFAULT NULL, `name` varchar(50) DEFAULT NULL, `age` int(11) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8
Sqoop导出语句
sqoop export --connect jdbc:mysql://localhost:3306/test \ --username root \ -P \ --export-dir '/user/hive/warehouse/test.db/user/000000_0' \ --table user\ --update-key id \ --update-mode allowinsert \ --input-fields-terminated-by '|' \ --input-null-non-string '\\N' \ --input-null-string '\\N' \ --lines-terminated-by '\n' \ -m 1
此时,这里创建的表没有主键,没有唯一非空字段,那么在使用allowinsert模式的时候,即使指定了--update-key的字段为id,那么在进行导出的时候,Sqoop也不会去检查id字段,而是直接选择insert语句进行插入。
如果想要实现数据库表中已经存在的数据就更新,不存在的数据就插入,那么就需要更改MySQL数据库中表字段的属性,如下:
CREATE TABLE `user_test` ( `id` int(11) NOT NULL, `name` varchar(50) DEFAULT NULL, `age` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8
此时就可以实现想要的操作了。
四、导出的相关问题
1、原子性
导出由多个线程并行执行。每个线程使用一个连接,单独的连接到数据库;这些线程之间有独立的事务。
Sqoop使用多行INSERT语法为每个语句插入最多100条记录。每100个语句事务提交一次,也就是每10,000行提交一次。这个机制避免了事务缓冲区无限制地增长导致内存不足的情况。
因此,导出的整个过程不具有原子性,每条数据还是具有原子性的。在导出完成之前,已经导出的部分是可见的。
2、导出失败
导出可能由于多种原因而失败:
1.从Hadoop集群到数据库的连接丢失(由于硬件故障或服务器软件崩溃)
2.尝试INSERT违反一致性约束的行(例如,插入重复的主键值)
3.试图解析HDFS源数据中的不完整或格式不正确的记录
4.尝试使用不正确的分隔符分析记录
5.容量问题(如内存不足或磁盘空间不足)
如果导出mapper任务因这些或其他原因而失败,则会导致导出作业失败。导出失败的结果未定义。每个导出mapper任务都在单独的事务中运行。此外,个别mapper定期执行提交当前事务。如果任务失败,当前事务将被回滚。任何先前提交的事务将在数据库中保持持久,从而导致部分完成的导出。
五、应用示例
一个基本的导出,将数据导出到bar表:
sqoop export --connect jdbc:mysql://db.example.com/foo --table bar \ --export-dir /results/bar_data
此示例将/results/bar_data中的文件内容插入到数据库中的bar表格中。目标表需要先在数据库中创建。Sqoop执行一组操作不考虑现有内容。如果Sqoop尝试在数据库中插入违反约束的行(例如,特定主键值已存在),则导出失败。foodb.example.comINSERT INTO
可以导出指定列--columns "col1,col2,col3"。
注意:未包含在--columns参数中的列需要定义默认值或允许NULL值。否则,数据库将拒绝导入的数据,从而导致Sqoop作业失败。
另一个基本导出,启用验证将数据导入bar表中:
sqoop export --connect jdbc:mysql://db.example.com/foo --table bar \ --export-dir /results/bar_data --validate
调用存储过程barproc导出/results/bar_data如下所示:
sqoop export --connect jdbc:mysql://db.example.com/foo --call barproc \ --export-dir /results/bar_data