Sqoop最佳实践

一、什么是Sqoop

Sqoop是一个在结构化数据和Hadoop之间进行批量数据迁移的工具,结构化数据可以是Mysql、Oracle等RDBMS。Sqoop底层用MapReduce程序实现抽取、转换、加载,MapReduce天生的特性保证了并行化和高容错率,而且相比Kettle等传统ETL工具,任务跑在Hadoop集群上,减少了ETL服务器资源的使用情况。在特定场景下,抽取过程会有很大的性能提升。

    如果要用Sqoop,必须正确安装并配置Hadoop,因依赖于本地的hadoop环境启动MR程序;mysql、oracle等数据库的JDBC驱动也要放到Sqoop的lib目录下。本文针对的是Sqoop1,不涉及到Sqoop2,两者有大区别,感兴趣的读者可以看下官网说明。

二、import

    import是数据从RDBMS导入到Hadoop的工具

    2.1、split

    Sqoop并行化是启多个map task实现的,-m(或--num-mappers)参数指定map task数,默认是四个。并行度不是设置的越大越好,map task的启动和销毁都会消耗资源,而且过多的数据库连接对数据库本身也会造成压力。在并行操作里,首先要解决输入数据是以什么方式负债均衡到多个map的,即怎么保证每个map处理的数据量大致相同且数据不重复。--split-by指定了split column,在执行并行操作时(多个map task),Sqoop需要知道以什么列split数据,其思想是:

    1、先查出split column的最小值和最大值

    2、然后根据map task数对(max-min)之间的数据进行均匀的范围切分

例如id作为split column,其最小值是0、最大值1000,如果设置4个map数,每个map task执行的查询语句类似于:SELECT * FROM sometable WHERE id >= lo AND id < hi,每个task里(lo,hi)的值分别是 (0, 250), (250, 500), (500, 750), and (750, 1001)。

    Sqoop不能在多列字段上进行拆分,如果没有索引或者有组合键,必须显示设置splitting column;默认的主键作为split column,如果表里没有主键或者没有指定--split-by,就要设置num-mappers 1或者--autoreset-to-one-mapper,这样就只会启动一个task。

    从上面的分析过程可以看到Sqoop以理想化方式根据split column将数据切分成多个范围,如果split键的值不是均匀分布,每个任务分配的数据量可能相差很大、导致数据倾斜。

    2.2、参数

--driver:指定JDBC驱动,默认Mysql

--table:指定查询的表

--columns:指定从源数据库导入的列。当没有设置--table参数,就默认查询表中所有字段,实现方式是在数据库执行一个查询语句,就可得到每个字段及其对应的类型.

--where:查询条件.如果设置了table参数,就以table、columns、where三个参数拼接成的SQL查询数据

--query:自定义查询SQL,语句要有$CONDITIONS关键字,作用是动态替换,当获取默认boundary query时,$CONDITIONS会替换成(1=1);获取查询的数据列和其对应的字段类型时$CONDITIONS会替换成(1=0)。table和query不能同时设置

--boundary-query:指定split的sql,如果没有设置,且有--table参数,生成的split sql是根据table、where条件拼出来的。

如果设置了--query参数,split sql是基于query sql的子查询:

 需要特别注意的是,有的数据库对子查询没有进行优化(如Mysql),查询性能会很低,这就要自定义boundary-query,提高查询效率。

    2.3、HDFS

数据直接导入到HDFS,按行读取并写入到HDFS文件,源表里的每一行数据在HDFS里作为单独记录,记录可以是文本格式(每行一个记录)或Avro、SequenceFile二进制格式。导入过程可以并行,因此可能生成多文件。

--append:生成的文件追加到目标目录里

--delete-target-dir:如果目标目录已经存在,会先把目录删掉,类似overwrite

  执行上面的命令后,可以看到详细的日志:输入数据是怎么split的、mapreduce执行进度、mapreduce的URL等

    2.4、Hive

--hive-import参数指定数据导入到hive表:

--target-dir:需要指定该参数,数据首先写入到该目录,过程和直接导入HDFS是一样

--hive-drop-import-delims:删除string字段内的特殊字符,如果Hive使用这些字符作为分隔符,hive的字段会解析错误、出现错位的情况。它的内部是用正则表达式替换的方式把\n, \r, \01替换成""

--null-string/--null-non-string:指定空字段的值。Sqoop默认空数据存的是“NULL"字符串,但hive把空解析成\N,因此当文件存储的空是默认的"NULL"字符串,hive就不能正常读取文件中的空值了

数据import到hive表的过程:完成源数据写入到hdfs后,就执行LOAD DATA INPATH命令把target-dir里的文件LOAD到hive表:

    2.5、Hbase

  --hbase-table指定数据直接导入到Hbase表而不是HDFS,对于每个输入行都会转换成HBase的put操作,每行的key取自输入的列,值转换成string、UTF-8格式存储在单元格里;所有的列都在同一列簇下,不能指定多个个列簇。

然后通过Hbase shell查看表数据量、数据,

参数详细说明:

--hbase-create-table:当HTable不存在或列簇不存在,Sqoop根据HBase的默认配置自动新建表;如果没有指定该参数,就会报异常

--hbase-row-key:指定row key使用的列。默认是split-by作为row key,如果没有指定,会把源表的主键作为row key;如果row key是多个列组成的,多个列必须用逗号隔开,生成的row key是由用下划线隔开(`ID`_`RUN_ID`)的字段组合

--column-family:指定列簇名,所有的输出列都在同一列簇下   

三、export

   export是HDFS里的文件导出到RDBMS的工具,不能从hive、hbase导出数据,且HDFS文件只能是文本格式。如果要把hive表数据导出到RDBMS,可以先把hive表通过查询写入到一个临时表,临时用文本格式,然后再从该临时表目录里export数据。

   3.1、task数

Sqoop从HDFS目录里读取文件,所以启动的map task数依赖于-m参数、文件大小、文件数量、块大小等,可以参考这篇文章

   3.2、插入/更新

   默认情况下Sqoop在数据导出到RDBMS时,每行记录都转换成数据库的INSERT语句,但也支持插入/更新模式,即根据一定规则判断,如果是新记录用INSERT语句,否则就UPDATE,设置--update-mode allowinsert参数启用该功能,插入/更新操作依赖于目标数据库。

    对于Mysql,使用INSERT INTO … ON DUPLICATE KEY UPDATE语法,用户不能指定列来判断是插入还是更新,而是依赖于表的唯一约束,Mysql在插入数据时,如果是因唯一约束引起的错误,就更新数据行。Sqoop会忽略--update-key参数,但要至少指定一个有效列,才能启用更新模式

    3.3、参数

   --columns:指定插入目标数据库的字段,sqoop直接读取hdfs文件并把记录解析成多个字段,此时解析后的记录是没有字段名的,是通过位置和columns列表对应的,数据库插入的sql类似于:insert into _table (c1,c2...) value(v1,v2...)

 --export-dir:指定HDFS输入文件的目录

 --input-fields-terminated-by:字段之间分隔符

 --input-lines-terminated-by:行分隔符

四、问题及优化

    4.1、Hive不支持的数据类型

 关系型数据库的字段类型和hive的字段类型还是有差别的,所以Sqoop有一个映射关系,把RDBMS中的类型映射到Hive类型。在create hive表时,会根据RDBMS类型和hive类型进行映射、进而设置hive表字段类型,如果没有匹配到,就会报异常,如VARBINARY:

    其解决方案有三种:

        1、在--query参数内显示对字段进行转换,如VARBINARY转换成VARCHAR,而Sqoop会默认的把VARCHAR转换成Hive的STRING.

        2、增加--map-column-hive参数,显示把字段映射到Hive指定的类型

        3、修改HiveTypes类,使Sqoop支持对特定类型(如:VARBINARY)的映射,这种方案相对以上两种可以一劳永逸,但要重编译sqoop源码

类型映射逻辑如下:

4.2、Java不支持的类型

    Sqoop创建ORM对象,数据库中的字段映射到Java属性,用于读取数据库ResultSet对象并解析字段,需要把数据库的类型映射到java类型,如果没有映射到,就会报错。解决方案也有三种:

        1、在--query参数内显示对字段进行转换

        2、设置--map-column-java参数

        3、修改ConnManager类

映射逻辑代码:

4.3、特殊字符

 当\t特殊字符导入到hive后,hive字段可能解析出错。解决方法是修改FieldFormatter类,使Sqoop可以删除或替换掉字段数据中包含\t的特殊字符:

4.4、字段错位

    使用--query和--columns参数时,如果columns设置的列顺序和query列顺序不同,会有个疑惑是import后的字段和实际字段的值不一样,这是因为从数据库查询的ResultSet对象序列化到实体对象时,column的值是根据索引取的。

例如readInteger的代码:

如果要改为columns的字段值是根据字段名取而不是根据索引位置取,可以更改一下几个地方的代码:ClassWriter、JdbcWritableBridge

本文首发于公众号:data之道

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352

推荐阅读更多精彩内容