Sqoop自定义多字节列分隔符

Sqoop提供的--fields-terminated-by选项可以支持指定自定义的分隔符,但是它只支持单字节的分隔符,对于我们特殊的需求:希望使用双字节的“|!”,默认的是不支持的。

Sqoop在进行每一次的导出任务时,都会调用codegen,生成一个java文件,并编译打包成一个jar,供MapReduce使用。这个java文件包装了一系列的对导出数据的访问接口,我们可以尝试通过对这个java文件进行分析,找到指定双字节分隔符的方法。

一般地,如果是使用的--query用查询语句获取数据,生成的文件为QueryResult.java,QueryResult.jar,如果使用的是--table,则用指定的表名对相应文件命名。java文件生成在sqoop脚本的同一目录下。

对于下面的Sqoop任务,生成test_table.java。


1 sqoop import --connect jdbc:XXX/testdb --username user --password password --table test_table --split-by id --fields-terminated-by ‘#‘

对于test_table.java做代码分析:

首先,看下分隔符的定义():


1 DelimiterSet(char field, char record, char enclose, char escape, boolean isEncloseRequired){……}//分隔符集定义
2
3 private final DelimiterSet __outputDelimiters = new DelimiterSet((char) 35, (char) 10, (char) 0, (char) 0, false);// 根据用户输入信息,定义当前分隔符

第一行,分隔符集定义,依次是 fields-terminated-by lines-terminated-by enclosed-by
escaped-by,最后一个如果为true,enclosed-by会应用到所有字段,如果为false,只对fields that embed
delimiters生效。

注意上面DelimiterSet定义语句,使用的是ASCII对照表。

所以,根据ASCII码表,第二行就很明白了,35是我们指定的‘#‘的ASCII码值。这里在研究的时候,用到一个trick,就是自己指定一个‘#’作为标记,然后从java代码里面找这个‘#’,它在哪里,就是我们关注的“有效”代码段。

如果,不指定分隔符为‘#’的话,上面的__outputDelimiters 是下面的样子。


1 private final DelimiterSet __outputDelimiters = new DelimiterSet((char) 44, (char) 10, (char) 0, (char) 0, false);// 默认分隔符

44即ASCII表示的‘,’。默认的分隔符,字段用逗号“,”,行用回车“\r\n”。

分隔符的定义搞明白了,接下来看下输出。Sqoop毕竟只是个中间处理环节,最后要数据到指定的目的地。经过几个toString跳转之后,看下面的代码段:


 1 public String toString(DelimiterSet delimiters, boolean useRecordDelim) {
2 StringBuilder __sb = new StringBuilder();
3 char fieldDelim = delimiters.getFieldsTerminatedBy();
4 __sb.append(FieldFormatter.escapeAndEnclose(id==null?"null":"" + id, delimiters));
5 __sb.append(fieldDelim);
6 __sb.append(FieldFormatter.escapeAndEnclose(app_no==null?"null":app_no, delimiters));
7 __sb.append(fieldDelim);
8
9 ………………
10 __sb.append(FieldFormatter.escapeAndEnclose(seq==null?"null":"" + seq, delimiters));
11 if (useRecordDelim) {
12 __sb.append(delimiters.getLinesTerminatedBy());
13 }
14 return __sb.toString();
15 }

这里fieldDelim从之前定义的分隔符集中获取了字段分隔符,然后拼字符串的方式,在每个字段值后面,附加一个字段分隔符。

看到这里,我们就有思路了,只要把这里fieldDelim的赋值做一下修改,赋值为“|!",就可以达到我们的目的。

于是,修改此处代码如下(也是唯一的一处修改):


1 public String toString(DelimiterSet delimiters, boolean useRecordDelim) {
2 StringBuilder __sb = new StringBuilder();
3 String fieldDelim = “|!”;
4 __sb.append(FieldFormatter.escapeAndEnclose(id==null?"null":"" + id, delimiters));
5 __sb.append(fieldDelim);
6 __sb.append(FieldFormatter.escapeAndEnclose(app_no==null?"null":app_no, delimiters));
7 __sb.append(fieldDelim);
8 …………
9 }

接下来,对修改的test_table.java进行编译。

关于如何编译的问题,通过跟踪sqoop import执行的时候的输出日志:


 1 14/05/23 10:55:07 WARN tool.BaseSqoopTool: Setting your password on the command-line is insecure. Consider using -P instead.
2 14/05/23 10:55:07 WARN sqoop.ConnFactory: Parameter --driver is set to an explicit driver however appropriate connection manager is not being set (via --connection-manager). Sqoop is going to fall back to org.apache.sqoop.manager.GenericJdbcManager. Please specify explicitly which connection manager should be used next time.
3 14/05/23 10:55:07 INFO manager.SqlManager: Using default fetchSize of 1000
4 14/05/23 10:55:07 INFO tool.CodeGenTool: Beginning code generation
5 14/05/23 10:55:08 INFO manager.SqlManager: Executing SQL statement: SELECT t.* FROM test_table AS t WHERE 1=0
6 14/05/23 10:55:08 INFO manager.SqlManager: Executing SQL statement: SELECT t.* FROM test_table AS t WHERE 1=0
7 14/05/23 10:55:08 INFO orm.CompilationManager: HADOOP_MAPRED_HOME is /usr/lib/hadoop
8 14/05/23 10:55:08 INFO orm.CompilationManager: Found hadoop core jar at: /usr/lib/hadoop/hadoop-core.jar
9 14/05/23 10:55:09 INFO orm.CompilationManager: Writing jar file: /tmp/sqoop-root/compile/3c33c9978c6103e610bf0f4a26fd92fa/test_table.jar
10 14/05/23 10:55:09 INFO mapreduce.ImportJobBase: Beginning import of test_table

通过上面高亮区域提供的信息,得到修改后的java代码的编译及打包方法如下:


1 javac -cp ./:/usr/lib/hadoop/hadoop-core.jar:/usr/lib/sqoop/sqoop-1.4.3.jar test_table.java
2 jar -cf test_table.jar test_table.class

打包完毕后,需要在使用Sqoop进行数据导出的时候,进行jar包的指定,指定的方式如下:


1 sqoop import --connect jdbc:XXX/testdb --username user --password password --table test_table --split-by id --jar-file /path/test_table.jar --class-name test_table

使用的选项为--jar-file和--class-name,其中--jar-file指定了jar的全路径,--class-name指定了用到的包中的java类。

以下是使用修改后的java文件实现的双字节分隔符导出结果。

再谈一下效率。使用这种自定义分隔符,自己手动生成的jar包做数据导入,测试数据5000w:

第一次:17min21sec

第二次:15min42sec

如果不指定jar,默认地执行,按照之前侧过的数据,分别是12min58sec,14min19sec。

看来使用这样的方式,还是对效率有一定的影响。但是也有可能是晚上7点数据库服务器做批量有关,后续要再做实验判定下。

时间: 2024-10-19 09:21:33

Sqoop自定义多字节列分隔符的相关文章

Hive insert into directory 命令输出的文件没有列分隔符分析和解决

参考资料:http://stackoverflow.com/questions/16459790/hive-insert-overwrite-directory-command-output-is-not-separated-by-a-delimiter 问题描述: Hive insert into directory 命令输出的文件没有指定列分隔符,输出结果就像变成了一个字符串. 通过CREATE EXTERNAL TABLE 和load 方式,尝试了多种分隔符都不能正确的区分,所有的字段内容

SharePoint 2013 关于自定义显示列表表单的bug

1.在SharePoint 2013中,我们隐藏列表Dispform页面的ListFormWebPart部件,转而使用自定义显示列表表单进行展示,因为这样更容易定制我们需要的显示: 2.之后发现文件夹下的文档,查看属性会报错,如下图: 3.报错如下图,没有项目: 4.不在文件夹下的项目,就没有这样的问题,怀疑是获取CurrentItem出问题了,但是没想到怎么去改xslt,如下图: 5.百度谷歌查找解决方案,最后找到定义列表模板,参考jianyi的博客,如下图: 6.然后发现处在文件夹下的ID为

jeecg3.5中为dgCol标签增加自定义格式化列中的数值的功能

jeecg3.5中只有formatter属性,只支持格式化时间格式的数据,不支持自定义格式化列中的值的功能,比如想把列中的的一个int或long型的值除以100转成float或doulbe值,jeecg3.5版本就不支持类似customFormatter这样属性,本文为jeecg3.5增加这个功能,主要修改的代码如下: org.jeecgframework.tag.core.easyui.DataGridColumnTag //51行  private String customFormatte

使用自定义的行分隔符,从StreamReader中读取一行字符串

在C#中,StreamReader的ReadLine方法是不支持自定义行分隔符的.这导致很多文本文件的行分隔符如果不是"\r"和"\n",那么使用StreamReader就无法正确读取到一行字符串. 所以我们这里采用一个.NET Core控制台项目,自定义一个ReadLineWithDelimiter扩展方法,演示使用自定义的行分隔符,从StreamReader中读取一行字符串,代码如下: using System; using System.IO; using S

easyui datagrid自定义按钮列,即最后面的操作列

在很多时候我们要在表格的最后添加一列操作列,easyUI貌似没有提供种功能,不过没关系,我们可以自定义来实现首先是HTML部分 <table id="tt" class="easyui-datagrid" style="width:100%;height:554px"             singleSelect="false"              fitColumns="true"    

自定义多列瀑布流功能

html代码 <!DOCTYPE html> <html> <head> <title></title> <meta /> </head> <body> <div class="flow" id="flow_container"></div> </body> </html> css代码 <style type=&quo

bootstrap自定义——栅格列数修改

从下载的bootstrap文件中找到less文件夹里面的variables.less,然后可以找到栅格列数进行修改 然后执行一下bootstrap.less,通过命令行,切换到其所在的目录D:\03 学习\前端视频2017-03-02\09. 移动web开发\03-移动web开发_03\资料\bootstrap-3.3.6\less,这个目录最好不要有中文,然后通过lessc 来编译bootstrap.less

shell自定义数组元素分隔符

公司人员命名图片习惯的加上了空格,今天有个需求,要批量下载这个写图片,这下蛋疼了 正确的url 是这样的 http://url  2119 01.jpg http://url  001.jpg 写到文本里就要加转义,变成 http://url\ 2119\ 01.jpg http://url\ 001.jpg 结果循环的结果就蛋疼了 #!/bin/bash pic=$(cat list.txt) for i in $pic do         echo "$i" done 结果 [[

easyUI——自定义操作列

项目中遇到一个问题:在easyUI的datagrid中增加一列为操作列.官网的demo中没有给出具体的解决问题的方法.解决的思路就是利用单元格的属性formatter中添加内容. HTML 分为两个部分,前面的部分是表格代码,后面的部分是明细弹出框. @*datagrid表格*@ <table id="dg" title="My Users" class="easyui-datagrid" style="width:700px;h