使用sqoop的关于单,多表操作以及--split-by&-m联合使用的注意事项

1 理论

sqoop如果不加入相关的参数,就只是单单的单个表的导入| 出,在生产中不适用,加入相关参数比如:-e|--query<同时替换--where、-table、--columns>就可以实现关联多张表的数据的导入|出了,引用官方一张截图如下:

接下来 -->>场景: 

sqoop import 
--connect jdbc:postgresql://...../..... 
--username jagel95  
--query 
"select * from retail_tb_order_qiulp_test where status = 'TRADE_FINISHED' or status = 'TRADE_CLOSED' or status = 'TRADE_CLOSED_BY_TAOBAO' and \$CONDITIONS" 
--hive-import -m 6 
--hive-table custom_analyse_db.retail_tb_order_main 
--hive-overwrite 
--hive-partition-key part 
--hive-partition-value qiulp 
--target-dir /user/qiulp/test1 
--split-by tid 


注意, retail_tb_order_qiulp_test表原有1000条数据,导入hive后成了6000条,是map=6产生的倍数关系。
仔细观察数据发现:一条数据在hive中有6条相同的数据: 

理解 
1.split-by 根据不同的参数类型有不同的切分方法,如int型,Sqoop会取最大和最小split-by字段值,然后根据传入的num-mappers来 确定划分几个区域。比如select max(split_by),min(split-by) from得到的max(split-by)和min(split-by)分别为1000和1,而num-mappers(-m)为2的话,则会分成两个区域 (1,500)和(501-1000),同时也会分成2个sql给2个map去进行导入操作,分别为select XXX from table where split-by>=1 and split-by<500和select XXX from table where split-by>=501 and split-by<=1000.最后每个map各自获取各自SQL中的数据进行导入工作。 

2.当split-by不是int型时出现如上场景中的问题。目前想到的解决办法是:将-m 设置称1,split-by不设置,即只有一个map运行,缺点是不能并行map录入数据。(注意,当-m 设置的值大于1时,split-by必须设置字段) 

3.split-by即便是int型,若不是连续有规律递增的话,各个map分配的数据是不均衡的,可能会有些map很忙,有些map几乎没有数据处理的情况【即产生数据倾斜】

 

2 实战

2-1,操作: 设置并行--num-mappers=4,加--split-by的情况会根据主键先查最大值和最小值,即:select min(key_id),max(key_id) from tb_oracle_stock_info_key。

        如tb_oracle_stock_info_key(股票信息表)中 key_id(主键)最小值为300,最大值为400,那么4个并行度的切片情况如下:

sqoop执行的日志记录如下:

select * from tb_oracle_stock_info_key where key_id between 300 and 325;

select * from tb_oracle_stock_info_key where key_id between 325 and 350;

select * from tb_oracle_stock_info_key where key_id between 351 and 375;

select * from tb_oracle_stock_info_key where key_id between 376 and 400;

小结:加--split-by 参数后,使用 >1 个并行时[这里操作是-m 4],效果理论上优于没有加--split-by 参数作业.

2-2 数据倾斜

  假设oracle的表tb_oracle_stock_info_key(股票信息表)主键为key_id,sqoop根据max(key_id)来平均分配4份。假设min(key_id)=1,max(key_id)=400,那么导数的时候会按400切割生4份,即 :

select * from tb_oracle_stock_info_key where key_id between 1  and 100;

select * from tb_oracle_stock_info_key where key_id between 101 and 200;

select * from tb_oracle_stock_info_key where key_id between 201 and 300;

select * from tb_oracle_stock_info_key where key_id between 301 and 400;

但是由于数据特殊的原因,key_id=[1,100]分区内自由1条数据,key_id=[101,300]内完全没有数据,99%数据都是key_id=[301,400],这样就会产生数据倾斜,也就是4个并行中,有3个不耗费时间,有1个花了大部分时间,这样的并行效果相当的不好[!!因此,在使用并行度的时候需要了解主键的分布情况是是否有必要的]:

另外:一般数据库表的关键字以数字类型为准,若为字符类型,最好能转换为数字类型,否则会报错或者切片不均!

2-3 案例

股票信息表tb_oracle_stock_info。该表数据量月500万,目前我们需要把数据翻倍且增加主键key_id,同时key_id的取值范围在1000万到2000万之间。建tb_oracle_stock_info_key1表:

create table tb_oracle_stock_info_key1

(

  key_id           number(16) primary key, --关键字id

  stock_code       varchar2(16),   --股票代码     

  timest           varchar2(8) ,   --日期

  open_price       number(30,2),   --开盘价元

  top_price        number(30,2),   --最高价元

  lowest_price     number(30,2),   --最低价元

  cloce_price      number(30,2),   --收盘价元

  deal_money       number(30,2),   --成交价元

  deal_volume      number(30,2)    --成交量股

) ;

 插入目标数据(只取主键1000万-2000万间的数据):

/*本语句的作用是

  1)把tb_oracle_stock_info表翻4倍

  2)ROWNUM 由1 自动递增,达到唯一,作为主键

  3)取主键1000万-2000万间的数据

*/

insert into tb_oracle_stock_info_key1 nologging

select t.*

  from (

        select ROWNUM as key_id,a.*

          from (select * from  tb_oracle_stock_info w

                union all

                select * from  tb_oracle_stock_info x

                union all

                select * from  tb_oracle_stock_info y

                union all

                select * from  tb_oracle_stock_info z

                ) a  

        ) t

 where key_id >10000000

   and key_id <=20000000

;

数据验证,表 tb_oracle_stock_info_key1供有1000万条数据,最小的key_id为10000001,最大的key_id为20000000:

执行 sqoop impoert:

sqoop import \

--connect "jdbc:oracle:thin:@ser_db:1521:orcl" \

--username db_oracle_user \

--password db_oracle_pass \

--table TB_ORACLE_STOCK_INFO_KEY \

--split-by key_id \

--target-dir /data/sqoop_datas/TB_ORACLE_STOCK_INFO_KEY0 \

--num-mappers 2 ;

运行效果:

 可以看到,作业是00:21:46分开始,之后两个map分别在00:24:46和0:24:49完成。不难理解,因为两个map数据量平均都是500万,因此完成两个map的完成时间很接近,且都大概花了3分钟左右。原因很简单,表 tb_oracle_stock_info_key1供有1000万条数据,最小的key_id为10000001,最大的key_id为20000000,作业又只分了2个并行度,因此在sqoop并行时分两个切片:
并行片1:只抽tb_oracle_stock_info_key1.key_id =[10000001,15000001]
并行片2:只抽tb_oracle_stock_info_key1.key_id =[15000001,20000000]
两个片的数据量基本相等,所以两个并行的map耗费的时候十分相近。

 

2-4 案例<倾斜>

目前tb_oracle_stock_info_key1的key_id为主键,目前key_id的取值范围为[10000001,20000000],即key_id<1000 万的情况是没有的。为了测试数据倾斜,插入一条key_id=1 的情况:

/*本语句的作用是

  1)把tb_oracle_stock_info表随机抽取1条

  2)ROWNUM 只取1,并把key_id 设置为1

  3)最后把这条数据插入到tb_oracle_stock_info_key1

*/

insert into tb_oracle_stock_info_key1 nologging

select 1 as key_id, a.*

  from tb_oracle_stock_info a

 where rownum =1

;

commit;

目前的数据分布情况:数据大部分都倾斜到[10000001,12000000]的区间。

执行:

设置两个并行度,并以key_id主键切片:

sqoop import \

--connect "jdbc:oracle:thin:@ser_db:1521:orcl" \

--username db_oracle_user \

--password db_oracle_pass \

--table TB_ORACLE_STOCK_INFO_KEY1 \

--split-by key_id \

--target-dir /data/sqoop_datas/TB_ORACLE_STOCK_INFO_KEY3 \

--num-mappers 2 ;

运行结果分析:

        由于min(key_id)=1和max(key_id)=2000万,那么两个并行的切片如下:
        并行片1:只抽tb_oracle_stock_info_key1.key_id =[1,1000000]
        并行片2:只抽tb_oracle_stock_info_key1.key_id =[10000001,20000000]
        所以并行片1只抽取1条数据,而并行片1需要抽取1000万条数据,最终执行情况如下:

      作业开始执行时间为19:56:20,然后分了2个并行度,因此有2个map,第一个map在19:56:50就执行完成,即只用了30秒就完成,原因很简单因为并行片1只有1条数据。第二个map在19:59:43完成,用了3-4分钟。这就是数据倾斜,2个并行度中,大部分数据在一个执行。

 

 

 

 

 

 

 

 

 

 

 

3 参考文章:

https://www.cnblogs.com/pejsidney/p/8962302.html

https://blog.youkuaiyun.com/weihua0722/article/details/78551070

感谢码友指正以及参考。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值