Sqoop作为一种重要的数据同步工具,在大数据中具有重要地位。在前期数仓建设中,尤其是DB库(MySQL)数据同步时,对Sqoop生产中遇到的常见问题进行总结并做好记录以便后续查看。
1、Sqoop 空值问题
Hive中的null在底层是以“\N”来存储,而MySQL中的null在底层就是null,这就导致了两边同步数据时存储不一致问题。Sqoop在同步的时候应该严格保证两端的数据格式、数据类型一致,否则会带来异常。
方法1:依赖自身参数
(1)导出数据时采用--input-null-string和--input-null-non-string两个参数。
(2)导入数据时采用--null-string和--null-non-string。
方法2:建表时修改hive底层存储,修改为''(空字符串)
在hive导出时,给需要导出的表创建一张临时表,该表和Mysql同步的表、字段、类型等严格一致,将需要导出的数据插入到该表中,在建立该临时表的时候将hive中Null底层存储“/N”修改为''(空字符串)。具体可添加下面代码
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe' with serdeproperties('serialization.null.format' = '')
例如:
drop table table_name;
CREATE TABLE IF NOT EXISTS table_name(
id int,
name string)
PARTITIONED BY (`partition_date` string)
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe' with serdeproperties('serialization.null.format' = '')
location 'hdfs://nameservice1/user/hive/warehouse/ods.db/table_name'
;
- 然后将需要导出的数据插入到该临时表中
- 最后采用sqoop导出命令将数据导出到Mysql
导入数据同理
我们sqoop导数任务采用的是第二种方案,虽然比较麻烦,但是能够减少sqoop带来的一些不必要的麻烦,而且也比较容易定位问题,逻辑清晰,sqoop导出的时候只需要写基本的导出命令即可,这样sqoop很容易做成一个通用的脚本来调度。
2、Sqoop 数据一致性问题
(1):如在Sqoop在导出到Mysql时,使用4个Map任务,过程中有2个任务失败,那此时MySQL中存储了另外两个Map任务导入的数据,此时运营正好看到了这个报表数据。而开发工程师发现任务失败后,会调试问题并最终将全部数据正确的导入MySQL,那后面老板再次看报表数据,发现本次看到的数据与之前的不一致,这在生产环境是不允许的。这是由于Sqoop将导出过程分解为多个事务,因此失败的导出作业可能会导致将部分数据提交到数据库。 这可能进一步导致后续作业由于某些情况下的插入冲突而失败,或导致其他作业中的重复数据。 您可以通过--staging-table选项指定登台表来解决此问题,该选项充当用于暂存导出数据的辅助表,分阶段数据最终在单个事务中移动到目标表。
--staging-table方式
(建立临时表,通过sqoop导入到临时表,成功之后再把临时表的数据通过事务导入到mysql的业务数据表,Sqoop在导入导出数据时,通过建立临时表可以解决好多问题,所以要学会巧用临时表),使用--staging-table选项,将hdfs中的数据先导入到临时表中,当hdfs中的数据导出成功后,临时表中的数据在一个事务中导出到目标表中(也就是说这个过程要不完全成功,要不完全失败)。为了能够使用staging这个选项,staging表在运行任务前或者是空的,要不就使用clear-staging-table配置,如果staging表中有数据,并且使用了--clear-staging-table选项,sqoop执行导出任务前会删除staging表中所有的数据。
注意:–direct导入时staging方式是不可用的,使用了—update-key选项时staging方式也不能用。
sqoop export --connect jdbc:mysql://ip:3306/dabases
--username root \
--password pwd \
--table table_name \
--columns id,name,dt \
--fields-terminated-by "\t" \
--export-dir "/user/hive/warehouse/db.ods/table_name_${day}" \
--staging-table table_name_tmp \
--clear-staging-table \
--input-null-string '\N' \
3、数据倾斜问题
sqoop的数据分割策略不够优秀导致的数据倾斜:
sqoop 抽数的并行化主要涉及到两个参数:num-mappers:启动N个map来并行导入数据,默认4个;split-by:按照某一列来切分表的工作单元。要避免数据倾斜,对split-by指定字段的要求是int类型同时数据分布均匀,满足这样的要求的表只有极少数的有自增主键的表才能满足。核心思想就是自己生成一个有序且均匀的自增ID,然后作为map的切分轴,这样每个map就可以分到均匀的数据,可以通过设置map的个数来提高吞吐量。
建议:
数据量500w以下使用4个map即可。
数据量500w以上使用8个map即可,太多会对数据库加压,造成其他场景使用性能降低。如果是为了专门导数据,和下游计算的并行度,可以适当调大。
例如:
通常可以指定split-by 对应的自增ID 列,然后使用–num-mappers或者-m指定map的个数,即并发的抽取进程数量。但是有时候会碰到很多的表没有添加自增ID或者,整数型的主键,或者 主键分布不均,反而会拖慢整个job的进程。
- 根据sqoop源码的设计,我们可以使用–query语句中添加自增ID,作为split-by的参数,与此同时通过设置的自增ID的范围可以设置boundary。
代码:
```bash
--query 方式:涉及参数 --query、--split-by、--boundary-query
--query: select col1、 col2、 coln3、 columnN from (select ROWNUM() OVER() AS INC_ID, T.* from table T where xxx ) where $CONDITIONS
--split-by: INC_ID
--boundary-query: select 1 as MIN , sum(1) as MAX from table where xxx
完整语法代码:
password-file通过 echo -n “password content” > passsword-file 方式得到,这样不会包含异常字符。
sqoop import --connect $yourJdbConnectURL \
--username $yourUserName
--password-file file;///localpasswordFile or hdfs relative Path
--query "" \
--split-by "" \
-m 8 \
-boundary-query “select 1 as min , sum(1) as max from table where xx” \
--other parames
参考链接:sqoop 并行抽取数据,同时解决数据倾斜_bymain的博客-CSDN博客
4、Map task并行度设置大于1的问题
并行度导入数据的 时候 需要指定根据哪个字段进行切分 该字段通常是主键或者是自增长不重复的数值类型字段,否则会报下面的错误。
错误日志:
Import failed: No primary key could be found for table. Please specify one with --split-by or perform a sequential import with ‘-m 1’.
就是说当map task并行度大于1时,下面两个参数要同时使用:
–split-by id指定根据id字段进行切分
–m n指定map并行度n个