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
感谢码友指正以及参考。