经过这么几天的折腾,发现 Sqoop1 真的比 Sqoop2 方便好用的多,Sqoop2 坑真是太多了,搞不定。Sqoop1 坑少也稳定,但是零基础使用过程中也是有几点需要注意的。
官方下载:Sqoop 官网
官方使用文档
Sqoop-1.4.6安装部署及详细使用介绍
如果像我一样直接用CDH里边自带的话,方便的地方是环境变量什么的不需要我再去配置了,很方便。
要检查安装成功没,直接 sqoop version
;
命令行使用
1 :
可能会出现警告:
accumulo does not exist! Accumulo imports will fail.
hcatalog does not exist! hcatalog imports will fail.
方法一:
CDH官方方法,如下:
Services that you want to use with Sqoop, such as HBase, Hive HCatalog, and Accumulo. When you run Sqoop, it checks to see if these services are installed and configured. It logs warnings for services it does not find. These warnings, shown below, are harmless. You can suppress these error messages by setting the variables $HBASE_HOME, $HCAT_HOME and $ACCUMULO_HOME to any existing directory.
即:只需要把 $HBASE_HOME, $HCAT_HOME and $ACCUMULO_HOME 设置为任意一个存在的目录即可。比如我:export ACCUMULO_HOME=/root
, 果然就不报警告了,非常简单方便,强烈推荐。而且使用方法二的话直接修改了配置文件很有可能会出问题,不安全
方法二:
如果不需要,可以屏蔽它;
到 “SQOOP_HOME/bin” 下编辑 configure-sqoop,注释掉里边与Accumulo 和 hcatalog 有关的代码块,不是太难,自己打开看看这个文件就知道了。
2:要注意 “–target-dir” 需要指定一个空的不存在的文件夹,不能指定已经存在的文件夹,它会自己创建的
3:一个 从 Oracle 到 HDFS 的 import 语句:
sqoop import --connect jdbc:oracle:thin:@<HOST>:1521:app --username user --P --query "select * from HD.STORE where \$CONDITIONS and RCVTIME < TO_TIMESTAMP('2017-05-30 00:00:00','yyyy-mm-dd hh24:mi:ss.ff')" --split-by id --direct --target-dir /user/root/store --m 1
这里边几点需要注意的坑:
“–connect “:CDH Sqoop1 使用 里边用一些 JDBC Connection Strings 的语法介绍和例子,但是 Oracle 的我试了不行,正确的写法应该如:
jdbc:oracle:thin:@<HOST>:<PORT>:<DATABASE_NAME>
,区别在于 thin 后边是需要又一个 冒号 的,很容易错,还有 与 中间的连接也是冒号,因为有的写法是 “/“,但是这里需要注意的是,这里是 冒号。“–query“:
- 如果这里SQL语句里边用过单引号了,那外边必须要用双引号
这里的SQL假如直接使用 “FROM STORE“的话,是会报 table 找不见,或者不存在的,因为使用“–query“的话没有指定 Schema ,所以这里必须使用 .
的形式where 后边必须有 $CONDITIONS 条件,sqoop 运行的时候,看日志发现sqoop 会在这里插入(1=0)或(1=1)来控制这条语句的执行。外边使用双引号的话,$CONDITIONS 前边需要加反斜杠 即:\$CONDITIONS。
Free form query in Sqoop Import with WHERE clause
“–split-by” :需要指定一个 int 类型的列名,一般是主键。sqoop 会计算这个字段的 MIN 和 MAX ,然后结合 fetchSize 来确定 怎么切分数据块。这个字段必填。
- “–direct“:没加这个之前,导入特别慢,中间经常会出现 “Connect reset“,这个没关系,一会儿它又回自动连接上。但是确实太慢了。使用这个字段后,导入速度又快又稳定,这个字段代表的意思应该是 使用的是关系数据库自带的导入导出工具。最好加上这个字段配置。
–options-file 使用
为了方便复用,我们可以把一串命令和参数写入txt文件中,使用 –options-file 来调用。详细使用方法,官方文档的 6.4 Using Options Files to Pass Arguments 里写的特别清楚了。因为是英文的,我这里简单给英文不好的同学说一下要点和更命令行方式对比需要注意的:
先上一个等同上面命令行例子的例子。
store_import.txt
#
# import options file of STORE table
#
#
# oracle table STORE to hive table *
#
import
--connect
jdbc:oracle:thin:@<HOST>:1521:app
--username
user
--password
123456
--query
select * from HD.STORE where $CONDITIONS and \
RCVTIME < TO_TIMESTAMP('2017-05-30 00:00:00','yyyy-mm-dd hh24:mi:ss.ff')
--split-by
FLOWNO
--direct
--target-dir
/user/root/store
--m
1
使用时,
$ sqoop --options-file /users/home/store_import.txt
- 文件要是 txt 文件形式
- 里边的参数和值的顺序跟命令行里的一行,但是都必须如上一样换行,一行一个
- 这里密码我们用了
--password
直接写入密码,方便自动执行;命令行里我们用的是-P
,用-P
为了安全,在命令行执行时需要手动输入密码,而且不会明文显示 变化较大的是
--query
参数后边的SQL语句,需要注意:- 参数和值一般一行一个,但是如果就像SQL语句一样,长的话可以在结尾使用反斜杠来换行
- 一般不要用双引号,除非双引号里边包含单引号,使用双引号的话就不能再使用换行符了,双引号必须在同一行。
- 这里的
$CONDITIONS
前边不再需要反斜杠来转换$
符号了
上面的例子都是从 oracle 导入 hdfs 的,现在我们用 sqoop 来直接导入 Hive。
导入数据到Hive
官方文档 7.2.12. Importing Data Into Hive
然后再上我的例子,
import
--hive-import
--hive-table
dw_hd.ods_store
--connect
jdbc:oracle:thin:@<HOST>:1521:app
--username
user
--password
123456
--query
select * from HD.STORE where $CONDITIONS and \
RCVTIME < TO_TIMESTAMP('2017-05-30 00:00:00','yyyy-mm-dd hh24:mi:ss.ff')
--split-by
FLOWNO
--direct
--target-dir
/user/root/store
--null-string
'\\N'
--null-non-string
'\\N'
--m
2
sqoop 导入 Hive 分三步:
1. 先导入--target-dir
指定的 HDFS 的目录中
2. 在 Hive 中建表
3. 调用 Hive 的 LOAD DATA INPATH
把 --target-dir
中的数据移动到 Hive 中
观察上边例子与前边的做对比,有以下几点不同:
--hive-import
:指定是导入 Hive--hive-table
:导入 Hive 中的数据库名和表名--null-string
和--null-non-string
:分别代表了 sqoop 对 字符串类型 null 值 和 非字符串类型 null 值 的处理。如果不指定的话,默认导入 Hive 后 字符串类型的 null 值是 ‘null’,非字符串类型 null 值是 ‘NULL’,这里用把这两种情况统一成了 ‘NULL’,sqoop 中用 ‘\N’,如果想要小写的 ‘null’ 的话,使用 ‘\N’。
问题1:导入后从Hive中查到的数据条数比实际从关系数据库中查到的条数多?
解决:原因是使用--hive-import
会使用默认的 Hive 的分隔符,值分隔符^A
和行分隔符\n
。官方文档 7.2.12. Importing Data Into Hive 里原文如下:
Hive will have problems using Sqoop-imported data if your database’s rows contain string fields that have Hive’s default row delimiters (\n
and \r
characters) or column delimiters (\01
characters) present in them. You can use the --hive-drop-import-delims
option to drop those characters on import to give Hive-compatible text data. Alternatively, you can use the --hive-delims-replacement
option to replace those characters with a user-defined string on import to give Hive-compatible text data. These options should only be used if you use Hive’s default delimiters and should not be used if different delimiters are specified.
Sqoop will pass the field and record delimiters through to Hive. If you do not set any delimiters and do use --hive-import
, the field delimiter will be set to ^A
and the record delimiter will be set to \n
to be consistent with Hive’s defaults.
这样问题就来了,如果导入的数据中有’\n’,hive会认为一行已经结束,后面的数据被分割成下一行。这种情况下,导入之后hive中数据的行数就比原先数据库中的多,而且会出现数据不一致的情况。
Sqoop也指定了参数 --fields-terminated-by
和 --lines-terminated-by
来自定义行分隔符和列分隔符。
可是当你真的这么做时 坑爹呀!
INFO hive.HiveImport: FAILED: SemanticException 1:381 LINES TERMINATED BY only supports newline ’\n’ right now.
也就是说虽然你通过--lines-terminated-by
指定了其他的字符作为行分隔符,但是hive只支持\n
作为行分隔符。
ORACLE中查询某个字段包含 回车 换行 符,
||
不是或,是 oracle 中的字符串连接符。%
是通配符,代表任意字符串。
查看是否包含 回车换行 符,即:\r\n
select * from system.test_tab1 where name like '%'||chr(13)||chr(10)||'%'
单独查看是否包含 回车换行 符,即:\r
select * from system.test_tab1 where name like '%'||chr(13)||'%'
单独查看是否包含 换行 符,即:\n
select * from system.test_tab1 where name like '%'||chr(10)||'%'
解决方法:简单的解决办法就是加上参数--hive-drop-import-delims
来把导入数据中包含的hive默认的分隔符去掉。这个最简单,如果确定数据中不该含有这些字符的话,或者确定去掉没影响的话,可以用这个。另外,使用这个就没法使用 --direct
选项了。
增量导入
7.2.9. Incremental Imports
问题:–incremental lastmodified 模式 不支持 导入Hive
--incremental lastmodified option for hive imports is not supported. Please remove the parameter --incremental lastmodified.
解决办法:使用导入HDFS 的方法,只不过 --target-dir
设置成 Hive table 在 HDFS 中的位置,类似于/user/hive/warehouse/store
,前提 Hive 中已经存在这个表了,不存在的话先CREATE。
sqoop incremental import to hive table
问题1:导入后每一行所有数据都在第一个字段里?
原因和解决:因为直接导入 HDFS 中 HIve 里的文件夹下的话,sqoop 默认给的 值分隔符 是逗号 ,
,而 Hive 默认值分割符是\001
,即:^A
,所以 Hive 是不认的,所以 需要 把值分隔符改成 ^A
,即加上下边的配置:
--fields-terminated-by
\001
问题2:还是上边那个问题,导入后从Hive中查到的数据条数比实际从关系数据库中查到的条数多?
原因和解决:这样子增量导入时,因为直接是导入 hdfs 的,不是 --hive-import
,所以就没法 用 --hive-drop-import-delims
去掉冲突的分隔符了。所以就又会出现上边那个问题。解决办法是:在 --query
后的SQL语句里做 replace 方法嵌套做替换,把\r
和\n
分别替换为空字符串, REPLACE(REPLACE(name,CHR(13),''),CHR(10),'')
,如下:
--query
select REPLACE(REPLACE(name,CHR(13),''),CHR(10),'') name,code from test
定义 sqoop job 增量导入
文档 12. Saved Jobs
用 sqoop job 做增量更新,它会在它的 metastore 中管理 --last-value
,很方便。
sqoop job --create test_import_store -- --options-file /root/sqoop_option_files/store_import.txt
这里有一个很弱智的坑,很容易栽进来,困了我快一天。
一般网上定义job的例子如下:
sqoop job --create user_info -- import --connect jdbc:mysql://mysql-server-ip:3306/sqoop --username root --password root --table user_info -m 1
看这里,sqoop job --create user_info -- import
,看到了吗,’–’ 和 ‘import’ 之间有一个空格,很容易忽视的一点。这里用 ‘–’ 来指示 job 的内容,第一次见,很容易把 –import 当成一个整体。不注意这个,就会一直报 不能识别的参数
ERROR tool.BaseSqoopTool: Unrecognized argument
这里我觉得,sqoop job 和 –options-file 一块用才更配,很方便去编辑 job,如下一样的模版,很容易避开上边那个坑。
sqoop job --create <job_name> -- --options-file <options-file-txt-path>
在metastore中保存密码
默认每次执行 job 时都需要手动输入密码。这样很不利于自动化任务和 Oozie 调度,所以需要设置在 sqoop metastore 中保存密码。
You can enable passwords in the metastore by setting sqoop.metastore.client.record.password to true in the configuration.
解决方式有两种:
第一种方式,使用password-file;
第二种方式,在sqoop-site.xml中添加如下属性即可(添加后第一次仍然需要输入密码 )。
<property>
<name>sqoop.metastore.client.record.password</name>
<value>true</value>
</property>
sqoop 常用命令
Sqoop详细介绍包括:sqoop命令,原理,流程
Sqoop使用手册
个人看法
我上面说的这些官方文档上都有,我也只是记录一下加深一下印象,顺便给英文不好的同学做下加工。能勉强看懂文档的,一定要自己去看。我崇尚面向官方文档编程,官方文档是最完整清晰和可信的,文档上看不懂的点再去 Google 或 Baidu 别人的看法。刚学习的新人更要看第一手的官方文档,避免走弯路。