主要讲解sqoop的运行原理,以及结合oralce的rownum作为拆分字段是所存在的问题,解决方案
1、sqoop的运行过程分析我们先设计一个简单的sqoop脚本
!/bin/shkinit -kt /etc/hyperbase1/conf/hyperbase.keytab hbase/tdh1
export JAVA_HOME=/usr/java/latest
export PATH=$JAVA_HOME/bin:$PATH
sqoop import \
--connect jdbc:oracle:thin:@${HSTAHOST}:${HSTAPORT}/${HSTASERVERNAME} \
--username root \
--password root \
--query "SELECT ROWKEY,ID,NAME,ADDRESS FROM (SELECT ROWNUM AS ROWKEY,T.* FROM PERSON T) WHERE \$CONDITIONS " \
--split-by ROWKEY \
--hive-database ods \
--hive-table person \
--target-dir ${HDFSODSPATH}/person\
--delete-target-dir \
--fields-terminated-by '\001' \
-m 4 \
--hive-drop-import-delims \
--null-string '\N' \
--null-non-string '\N' \
--compression-codec org.apache.hadoop.io.compress.BZip2Codec
首先将--query查询的sql语句-m所配置的数量来进行切分,如上配置的map并发任务数为4个,那么sqoop会将查询的sql语句拆分成4个区域。
切分规则按照其中一个关键的参数--split-by 字段名,sqoop建议选取自增的int类型字段或者表的主键作为拆分字段。如果拆分字段的数据类型为int类型,split会获取作为切分字段的最大值和最小值,然后根据-m需要划分的map任务数来划分区域,拆分后的区域分配到不同的map任务,每个map任务在读取数据库中的一行一行的数据值,抽取到HDFS中。
例如:拆分字段的最小值为1,最大值为1000,map任务书为2个,那么会拆分成(1,500),(501,1000)两个区域,分配到两个不同的map任务中执行。
2、 使用oralce的number作为sqoop的拆分字段存在的问题首先oracle的number可以获取oracle的序列号,而且是自增的唯一数字类型字段,原理上是可以用来作为切分字段,但现在如果将map任务设置为多个情况下,会出现的问题就是,数据抽取完成后总数据量与数据源(oracle)是相同的,但是当我们查询数据内容时,会发现存在数据重复和缺失的现象,解释这个现象需要知道sqoop在切分和抽取的过程中,都做了些什么:结合具体的数据来进行解析就是:
由上面的数据抽取过程,我们显而易见的可以的出一个结论,直接使用rownum作为切分字段时,分配到每个map任务的执行sql语句,在每次获取的rownum所对应的值的顺序是各不相同的,最终就会导致数据有重复和缺失的现象。
那么现在数据抽取出现错误数据的问题我们应该怎么去解决呢,那么我们现在可以假设,假如我们按照其中的一个字段进行数据排序,保证每次查询的数据都能够保证顺序是一致的,那么是否就能解决这个问题呢?那么我们现在就进行数据抽取测试,来验证我们的假设是否成立
测试1:
首先测试任意的一个字段进行排序,在获取rownum之前,先按照非主键的字段(ID)进行排序,再将rowkey作为切分字段进行数据抽取,将--query的查询语句修改为
--query "SELECT ROWKEY,ID,NAME,ADDRESS FROM (SELECT ROWNUM AS ROWKEY,T.* FROM (SELECT * FROM PERSON ORDER BY ID )T) WHERE \$CONDITIONS " \
那么他的运行过程以及结果是这样的测试结果显示并没有正确的获取数据,这个原因是oracle的order by在对数据进行排序的过程中,同一个排序字段的值的顺序仍然不是固定不变的,所以在切分成4个区域获取rownum的过程中,仍然存在rownum取到同一个值小2的现象。
测试2:
order by字段为表的主键字段,在获取rownum之前,先按照主键的字段(ID)进行排序,再将rowkey作为切分字段进行数据抽取,将--query的查询语句修改为
--query "SELECT ROWKEY,ID,NAME,ADDRESS FROM (SELECT ROWNUM AS ROWKEY,T.* FROM (SELECT * FROM PERSON ORDER BY ID )T) WHERE \$CONDITIONS " \
那么测试结果:经过主键排序以后,可以保证了每个map任务每次查询的rowkey可以对应相同的一条数据,然后我们在进行数据抽取,就会发现数据量正确并且查找数据内容也跟源数据库保持一致。总结--split-by 与oracle的rownum结合使用的过程中,需要注意rownum的获取原理,rownum并非表中的某一列,oracle每次查询的结果顺序不同,那么rownum的获取结果也不相同,在进行拆分区域和sql语句到每个map任务执行时,所查询的数据也会有交叉的部分存在,那么避免这种现象产生的方法:
- 将map任务数设置为1,在数据量比较少,并且在抽取效率要求不高的时候可以采取
- 在获取rownum之前,先将数据按照主键进行排序,(建议使用)
- 表单一主键的情况下,可以将主键字段作为切分字段
阅读全文: http://gitbook.cn/gitchat/activity/5d77aee5d123d702c4c3375c
您还可以下载 CSDN 旗下精品原创内容社区 GitChat App ,阅读更多 GitChat 专享技术内容哦。