搞大数据的人,基本都绕不开这么件事:业务数据在MySQL里躺着,数仓在HDFS上等着分析,中间这一公里怎么打通?我早年最早用的是自己写Java程序起多线程跑JDBC,后来换成Sqoop才发现,这玩意把并行导入、类型映射、增量同步这些事全内置好了,是真的省事。这篇内容我基于实际项目里用Sqoop从MySQL往HDFS导数据的经验,把原理、配置、调优和踩过的坑一次讲清楚,适合刚接触Sqoop的同学,也适合已经在用但想搞懂并行机制的老手。
1. Sqoop是什么:为什么还在用它做MySQL到HDFS的导入
1.1 先搞清楚Sqoop在数据链路里的位置
Sqoop是Apache下面一个专门做结构化数据与Hadoop生态之间批量迁移的工具,全称是SQL to Hadoop。说直白点,它就是一条数据传输管道,一头接MySQL、Oracle、PostgreSQL这些关系型数据库,另一头接HDFS、Hive、HBase这类大数据存储。在实际数仓项目里,它最常见的任务就是"把MySQL里的业务表定期搬到HDFS上,供后续ETL和离线分析使用"。
很多人会问,都0202年了,Flink CDC、DataX这些工具不也挺火,为什么还在用Sqoop?我的看法是:Sqoop对传统离线批处理场景依然有不可替代的价值。它不依赖实时流框架,不用改业务库的binlog配置,一个命令就能拉全量,配好增量参数就能做增量,部署成本极低。对于数据量在TB级别以下、对时效性要求不高(T+1)的离线任务,Sqoop是最稳、最省心的选择。DataX在异构数据源支持上更广,Flink CDC在实时性上更强,但它们的上手成本和运维复杂度都更高。选型这件事没有绝对的对错,关键看你的场景。
1.2 Sqoop的架构和两个核心组件
Sqoop的架构其实不复杂,它本质上是一个MapReduce客户端。你执行一条import命令,Sqoop会做三件事:先通过连接器(Connector)跟MySQL通信,拿到表的元数据(字段名、类型、主键信息),然后根据参数生成对应的MapReduce作业,提交到Hadoop集群上执行,最后由各个Map任务并行去MySQL拉数据、写到HDFS指定目录。
这里有两个核心组件你要理解。一个是连接器(Connector),它负责跟具体数据库打交道,MySQL对应的是MySQL Connector,底层是JDBC。另一个是管理器(Driver),它负责把导入流程编排成MapReduce作业。Sqoop 1和Sqoop 2架构差别很大,Sqoop 2是B/S架构,有Server和Web UI,但实际生产环境里绝大多数团队用的都是Sqoop 1,命令行直接干活,稳定可靠。我这里讲的也全部是Sqoop 1的用法。
2. 环境准备:MySQL、HDFS和Sqoop三方联调
2.1 MySQL端的准备工作
在跑任何导入命令之前,MySQL那边有三件事必须先确认,少了任何一件都会在后面出幺蛾子。
第一件事是账号权限。Sqoop导入需要至少对目标表有SELECT权限,如果后面要用增量导入的lastmodified模式,还需要能读表结构信息。我习惯在MySQL里单独建一个专用账号,而不是直接用root,这样权限可控,也方便排查问题。比如:
CREATE USER 'sqoop_user'@'%' IDENTIFIED BY 'your_password'; GRANT SELECT ON your_db.* TO 'sqoop_user'@'%'; FLUSH PRIVILEGES;第二件事是确认JDBC连接串能通。很多新手上来就报"Connection refused"或者"SSL connection error",排查半天最后发现是MySQL 8默认的认证插件问题。MySQL 8.0默认用的是caching_sha2_password,而Sqoop自带的JDBC驱动版本如果比较老,只支持mysql_native_password,两边握手失败,连接就断。解决方法很简单,要么把MySQL用户的认证插件改回mysql_native_password,要么在Sqoop lib目录下换一个新版的MySQL Connector/J驱动。我在CentOS环境里实测,mysql-connector-java-8.0.30这个版本配合MySQL 8.0很稳。
第三件事是确认表上有主键或唯一索引。这一点直接关系到并行导入能不能跑起来,后面讲原理的时候会细说。简单说就是,Sqoop要根据主键把数据切分成多个分片,如果表没有主键,你就得用--split-by指定一个合适的列,否则任务直接报错。
2.2 HDFS端的准备
HDFS那边相对简单,主要是确认目标目录存在且可写。Sqoop不会帮你自动创建多层目录,如果你指定了一个不存在的目录,有些版本会报错,有些会自己创建,行为不完全一致。我建议提前手动建好:
hdfs dfs -mkdir -p /data/ods/user_info hdfs dfs -chmod 755 /data/ods/user_info另外要确认运行Sqoop的机器上配置好了Hadoop的客户端环境,也就是HADOOP_HOME环境变量要指向你的Hadoop安装目录,core-site.xml、hdfs-site.xml这些配置文件要能被加载到。判断标准很简单:在命令行执行hdfs dfs -ls /能正常列出目录,说明Hadoop客户端没问题。很多人Sqoop装好了,一跑就报找不到HDFS文件系统,十有八九是这一步没做好。
还有一点容易被忽略,Sqoop提交的MapReduce作业需要YARN集群有空闲资源。如果你只有一个节点,内存给得又小,跑大批量导入时Map任务可能会一直Pending。我在测试环境遇到过,三个Map任务卡了十分钟都没启动,最后一看是YARN的可用内存不够。这个问题在实际排障里出现频率很高,后面章节会专门讲。
2.3 Sqoop安装与JDBC驱动
Sqoop的安装本身不复杂,下载二进制包解压就行,但要注意版本匹配。Sqoop 1.4.7是目前用得最多的稳定版,它跟Hadoop 2.x、3.x都兼容。把安装包解压到/opt/sqoop,然后设置环境变量:
export SQOOP_HOME=/opt/sqoop export PATH=$PATH:$SQOOP_HOME/bin export HADOOP_HOME=/opt/hadoop关键一步是把MySQL的JDBC驱动jar包放到Sqoop的lib目录。注意,Sqoop 1.4.7默认的lib目录里没有MySQL驱动,你必须自己放。我用的是mysql-connector-java-8.0.30.jar,放到/opt/sqoop/lib下面,然后验证:
sqoop version如果能看到Sqoop的版本信息,并且没有报缺类,说明环境基本OK。这里有个细节:如果Sqoop和Hadoop在同一台机器上,注意CLASSPATH里不要出现多个版本的JDBC驱动,否则会加载到错误的类,报各种莫名其妙的方法找不到异常。我之前就吃过这个亏,系统里有个老的mysql-connector-java-5.1.49,Sqoop加载了旧版本,连MySQL 8的时候SSL握手一直失败。
3. 并行导入原理:数据是怎么被拆成多份的
3.1 为什么能并行:Sqoop的分片机制
Sqoop导入MySQL数据到HDFS,核心能力就是并行。但并行不是凭空来的,它的底层逻辑是:把一张整表的数据切分成N个互不重叠的分片(Split),每个分片由一个Map任务负责拉取。N就是你在命令里用-m参数指定的Map任务数。
分片是怎么切出来的?Sqoop默认的做法是:取表的主键列(或者用--split-by指定的列),先通过一个SELECT MIN(split_col), MAX(split_col) FROM table的查询拿到该列的最小值和最大值,然后把最小值到最大值这个区间均匀切成N份,每份作为一条WHERE条件,生成N个查询。比如主键从1到1000,-m设成4,那么四个Map任务分别跑的是WHERE id >= 1 AND id < 251、WHERE id >= 251 AND id < 501等等。
这里有个关键前提:分片列的数据类型必须是数值型或者日期型,因为Sqoop要做区间算术。如果分片列是字符串,Sqoop也能处理,但效率会差很多,它会把字符串转换成哈希值再切分,分出的区间不一定均匀。所以你在设计表结构的时候,如果提前知道这表要用Sqoop导,最好让主键是自增整数,这是最理想的情况。有些老表主键是UUID字符串,那就很头疼,后面优化章节我会讲怎么处理。
3.2 --split-by与--boundary-query的原理
当表没有主键,或者主键不适合做分片时,就需要用--split-by手动指定分片列。比如一张订单表,主键是联合主键(order_id, item_id),Sqoop取MIN和MAX的时候会出问题,因为它默认取第一个主键列。这时候你就得显式指定:
--split-by order_id--split-by的原理还是那条MIN/MAX查询,只是把列换成了你指定的列。它能解决"没有主键"的问题,但解决不了"列数据分布不均"的问题。比如分片列是status字段,值只有0和1两种,MIN是0,MAX是1,区间切分出来就失效了。遇到这种情况,就得用--boundary-query来手动指定边界查询。
--boundary-query的作用,是替代默认的SELECT MIN(split_col), MAX(split_col) FROM table。你可以自己写一条更高效的查询,甚至可以直接写死边界值。比如:
--boundary-query "SELECT 100000, 900000 FROM dual"这样Sqoop就不会去扫描全表算MIN和MAX,而是直接用你给的边界值做分片。在大表场景下,默认的MIN/MAX查询MySQL会做全表扫描(除非有覆盖索引),几千万行的表光这一步就要好几秒甚至更久。我一般都会显式指定--boundary-query,用explain确认能走索引的写法。另外,boundary-query还可以结合--split-by做均匀分片的预处理,比如用SELECT MIN(id), MAX(id) FROM table WHERE create_time >= '2024-01-01'这种条件缩小数据范围。
3.3 并行度与连接数的关系
理解了分片机制,你就明白了一个道理:-m设多少,MySQL那边就会同时打开多少连接。每个Map任务一个JDBC连接,每个连接执行一条带WHERE条件的SELECT。所以并行度不是越大越好,要综合考虑三方面:
一是MySQL能承受的连接数和查询压力。生产环境的MySQL上还有业务在跑,你一下开20个连接做全表扫描,轻则拖慢业务,重则把数据库连接池打满。我在一个线上项目里就因为这个被DBA找过,后来学乖了,非高峰时段跑,-m控制在8以内。
二是每个分片的数据量要均匀。分片数据量不均匀叫数据倾斜,4个Map任务里3个秒完,1个跑半小时,整体速度被拖死。数据倾斜的根源通常是分片列的值分布不均,后面优化章节详细讲。
三是Map任务本身的资源开销。每个Map任务在YARN上要占一个Container,内存默认1GB左右。-m设成20,就要占20GB内存。如果你的集群资源有限,任务会一直处于ACCEPTED状态排队,反而更慢。所以并行度的选择,本质是数据库压力、集群资源、数据分布三者的平衡。
4. 核心参数配置详解
4.1 必选参数与数据格式参数
Sqoop的import命令参数很多,但真正每天都要用的就那么几个。我按用途分组说一下,这样你记忆负担小。
最基本的导入命令长这样:
sqoop import \ --connect jdbc:mysql://192.168.1.100:3306/business_db \ --username sqoop_user \ --password your_password \ --table orders \ --target-dir /data/ods/orders \ --split-by order_id \ -m 4这里的--connect指定JDBC连接串,格式是jdbc:mysql://主机IP:端口/数据库名。--username和--password是MySQL账号。--table指定要导的表名。--target-dir是HDFS上的目标目录,注意这个目录不能事先存在(除非加了--append参数),否则Sqoop会报"Target directory already exists"的错误,这是新手最容易踩的坑之一。--split-by和-m是并行导入的黄金组合,缺了--split-by,表又没有主键,任务直接失败;缺了-m,默认只起4个Map任务。
数据落地格式用--as-textfile指定,这是默认格式,每行一条记录,字段间用逗号分隔。如果你对接的是Hive表,通常加--hive-import直接导到Hive仓库。我更推荐的做法是先导成文本落到HDFS,再用Hive的LOAD DATA INPATH把数据加载进表,这样链路更清晰,也方便排查数据问题。
4.2 字段分隔符与空值处理
文本格式下,字段分隔符默认是逗号,但业务数据里经常有包含逗号的字段,比如地址"北京市,朝阳区",导出来就会错位。解决方法是换一个不常见的分隔符,比如\001(Sqoop的--fields-terminated-by参数支持Hive的\001写法)。这也是Hive表最常用的字段分隔符,直接对接零成本。
--fields-terminated-by '\001' \ --lines-terminated-by '\n'空值处理也是个隐蔽的坑。默认情况下,MySQL的NULL值导入到HDFS文本里会变成字符串"null",不是空字符串。如果你后续要用Hive查询,WHERE field IS NULL会查不到这些记录,因为它们存的是字符串"null"。解决办法:
--null-string '\\N' \ --null-non-string '\\N'\N是Hive约定俗成的NULL表示方式。加上这两个参数后,MySQL的NULL在落地文件里就是\N,Hive读进来会正确识别为NULL。这是我在生产环境优化Hive数据质量时最常用到的一招。
4.3 增量导入参数
全量导入简单粗暴,但业务表越来越大后,每次全量拉几千万行肯定不现实。这时候要用增量导入。Sqoop提供两种增量模式,用--incremental指定。
第一种是append模式,适用于主键单调递增、只追加不更新的场景,比如日志表。它通过--check-column id和--last-value 10000两个参数控制,含义是"把id大于10000的数据拉下来"。增量条件最终会变成WHERE id > 10000,简单直接。
sqoop import \ --connect jdbc:mysql://192.168.1.100:3306/business_db \ --username sqoop_user \ --password your_password \ --table user_log \ --target-dir /data/ods/user_log \ --incremental append \ --check-column id \ --last-value 10000 \ -m 4第二种是lastmodified模式,适用于有update_time字段、数据会被修改的表。它会同时带两个条件:WHERE update_time > '上次时间',并且把新数据追加到目标目录。这个模式有个限制,目标目录必须跟上次导入保持一致,不能换目录,否则增量数据会丢。
增量导入有一个很关键的实操细节:--last-value的值不是手动拍脑袋的,而是要有记录机制。我之前见过有人写脚本里硬编码last-value=20240101,跑完不去更新,第二次跑就漏数据。建议把每次导入的最大值写到HDFS或者MySQL的元数据表里,脚本自动读取上次的值,这样才安全。
5. 实操:从建表到HDFS落地完整流程
5.1 准备测试数据
光说不练假把式,我带你把整个流程走一遍。假设我们要导一张用户表,先登录MySQL建表并插入测试数据:
CREATE DATABASE IF NOT EXISTS test_db; USE test_db; CREATE TABLE user_info ( id INT NOT NULL AUTO_INCREMENT, user_name VARCHAR(50), age INT, city VARCHAR(100), create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; INSERT INTO user_info (user_name, age, city) VALUES ('张三', 25, '北京市'), ('李四', 30, '上海市'), ('王五', 28, '广州市'), ('赵六', 35, '深圳市');这里有个容易踩的坑:如果表是utf8mb4编码,而Sqoop的JDBC连接串没有指定字符编码,导入到HDFS的中文可能会变成乱码。JDBC连接串里要加上characterEncoding=utf8,比如:
--connect "jdbc:mysql://192.168.1.100:3306/test_db?characterEncoding=utf8&useSSL=false"useSSL=false可以绕开MySQL 8的SSL认证问题,后面排查章节还会再提。
5.2 执行导入命令
数据就绪后,执行导入命令。这里我故意把参数写全一点,方便你对照:
sqoop import \ --connect "jdbc:mysql://192.168.1.100:3306/test_db?characterEncoding=utf8&useSSL=false" \ --username sqoop_user \ --password your_password \ --table user_info \ --target-dir /data/ods/user_info \ --split-by id \ -m 2 \ --fields-terminated-by '\001' \ --null-string '\\N' \ --null-non-string '\\N'执行过程中,你会看到Sqoop打印一系列日志。关键信息有几处:先是打印Retrieving imported table metadata,说明它在读表结构;然后是Tool.cmdLine相关的参数回显;再往下会看到MapReduce作业提交,进度条开始跑。如果分片成功,你会看到Splits: 2这样的信息,说明数据被切成了2片,对应2个Map任务。
这里提醒一句,Sqoop的密码直接写命令行里,会在进程列表和Shell历史里暴露,生产环境不安全。可以用--password-file参数,把密码放到HDFS上的一个文件里,并修改文件权限为400,Sqoop运行时会从文件里读密码。具体做法:
echo -n "your_password" > /tmp/sqoop_pwd.txt hdfs dfs -mkdir -p /user/sqoop/ hdfs dfs -put /tmp/sqoop_pwd.txt /user/sqoop/pwd.txt hdfs dfs -chmod 400 /user/sqoop/pwd.txt然后命令里写成--password-file /user/sqoop/pwd.txt,注意这个路径是HDFS路径,不是本地路径。
5.3 验证HDFS结果
导入完成后,验证落地数据。先看文件列表:
hdfs dfs -ls /data/ods/user_info会看到两个part文件,因为-m设的是2。每个part文件就是对应Map任务写入的结果。用hdfs dfs -cat查看内容:
hdfs dfs -cat /data/ods/user_info/part-m-00000正常情况你会看到类似这样的输出:
1 张三 25 北京市 2024-05-20 10:30:00.0 2 李四 30 上海市 2024-05-20 10:31:00.0字段之间是\001(Tab键的ASCII码1),在cat显示里看起来像空格或者特殊字符,这是正常的。如果你想一目了然地确认分隔符,可以用cat -A或者od -c查看原始字节。我习惯用hdfs dfs -text,它会自动把二进制转成文本,比较直观。
还要验证一下数据量是否对得上,用HDFS的行数统计:
hdfs dfs -cat /data/ods/user_info/part-* | wc -l这个结果应该等于MySQL里SELECT COUNT(*) FROM user_info的值。如果不一致,就是导入过程中有数据丢失,后面排查章节讲怎么定位。
6. 性能优化:从300秒压到45秒的调优实录
6.1 先找准瓶颈
优化之前一定要先定位瓶颈,别上来就调参数。我就见过有人把-m从4调到20,结果MySQL被打挂了,整个导入彻底失败。定位瓶颈一般看三个阶段的时间:Metastore阶段(读元数据、算分片)、Map阶段(拉数据)、Reduce阶段(写文件)。Sqoop默认没有Reduce阶段,所以重点看Map阶段。在YARN的ResourceManager页面上,你能看到每个Map任务的启动时间、运行时长、读取的数据量,哪个任务慢就点进去看它的日志。
实际项目里最常见的瓶颈是两类:一类是MySQL单条查询慢,表现为某个Map任务长时间卡在Running状态,打开日志看到在等MySQL返回;另一类是网络带宽打满,表现为所有Map任务运行时间差不多,但整体吞吐上不去。两者的优化思路完全不同,前者要优化分片和索引,后者要调整并行度和压缩。
6.2 具体优化手段
第一板斧是优化分片。分片不均匀是性能杀手,调优的核心就是让每个Map任务处理的数据量接近。做法是用--boundary-query手动计算均匀的边界,或者改用分布更均匀的列做split-by。我之前遇到一张用户表,id是自增主键,但早期数据有大量删除,id=1000到5000这一段是空的,导致--split-by id切出来的分片,有的只有几百行,有的有上百万行。后来我在boundary-query里用子查询把id做了重映射,让分片按实际行数切,性能立刻翻倍。
第二板斧是加索引。Sqoop生成的查询是WHERE split_col >= ? AND split_col < ?,这种范围查询如果分片列上没有索引,MySQL要做全表扫描。所以强烈建议在分片列上建索引,特别是大表。建索引的SQL很简单:
ALTER TABLE big_table ADD INDEX idx_id (id);如果是按时间字段增量导入,一定要在时间字段上建索引,否则每次增量都是全表扫,数据量大了以后全量扫描的时间会越来越长。
第三板斧是增大每次拉取的数据量。Sqoop有--fetch-size参数,控制每次JDBC从MySQL读取的行数,默认是1000。在带宽够的情况下,适当调大到5000或10000,能减少网络往返次数。我实测过,从默认1000调到10000,4个Map任务的总耗时能降20%左右。但要注意,设太大会占用Map任务JVM的堆内存,我一般配合-Dmapreduce.map.memory.mb=2048一起用。
第四板斧是开压缩。落盘前先压缩,能大幅减少HDFS的IO和网络传输。Sqoop支持--compress参数,默认压缩方式是gzip,也可以指--compression-codec org.apache.hadoop.io.compress.SnappyCodec指定Snappy。Snappy压缩速度快、压缩比适中,是数仓场景的首选。开启后,part文件会变成part-m-00000.snappy,Hive读的时候自动识别,不需要额外处理。
6.3 一个千万级数据的优化案例
说个真实的优化案例,一张订单表,数据量1800万行,大小约8GB,从MySQL往HDFS导。初始配置是-m 4、--split-by id、无索引、无压缩,跑完耗时约300秒。我做了三步优化:
第一步,在id列上确认已有索引,没有的话补上。第二步,把-m从4调到8,同时把MySQL的max_connections临时放宽,避免连接数不够报错。第三步,加上Snappy压缩,把--fetch-size调到5000。三步做完,同一张表再跑一次,耗时降到45秒左右,HDFS上的存储占用也从8GB降到2.2GB。这个案例充分说明,并行度、索引、压缩三管齐下,效果立竿见影。
但我也要泼一盆冷水:不是所有场景都适合开高并行度。有一次我导一个MySQL从库上的表,-m设成12,直接导致从库主从延迟飙到几百毫秒,业务侧还有查询在用这个库,吓得我赶紧停了任务。从那以后,我给自己定了一条规矩:生产环境MySQL的Sqoop导入,-m默认不超过8,跑之前先看主从延迟指标,延迟超过阈值就降级。
7. 常见问题与排查技巧
7.1 Sqoop连接不上MySQL
这是群里被问得最多的问题,报错通常是Connection refused或者Communications link failure。先别急着改Sqoop参数,按顺序排查:第一,MySQL端口是否对外开放,用telnet 192.168.1.100 3306测一下,通不通一目了然。第二,MySQL的用户是否允许从Sqoop所在机器登录,GRANT语句里'%'和具体IP区别很大。第三,JDBC连接串里IP、端口、库名有没有写错,注意jdbc:mysql://后面跟的是MySQL所在的机器,不是Sqoop所在机器。第四,账号密码对不对,这个最简单也最容易犯低级错误。排查完还不行,看MySQL的错误日志/var/log/mysql/error.log,那里通常记录了拒绝连接的真实原因。
7.2 SSL连接错误
MySQL 8默认开启SSL,Sqoop连接时如果JDBC驱动不支持,或者两边SSL配置不匹配,会报SSL connection error。最简单的处理方式是在JDBC连接串里加useSSL=false。如果公司安全规范要求必须用SSL,那就得在连接串里指定sslMode=REQUIRED,同时把MySQL的CA证书放到Sqoop的信任库,配置-Djavax.net.ssl.trustStore=/path/to/truststore。不过从我的实践经验看,大多数内网数据同步场景用不着SSL,直接禁用掉省心。另外,MySQL 8还用caching_sha2_password认证,老版本JDBC驱动连不上,解决办法前面讲过,换新版驱动或改用户认证插件。
7.3 主键为空或重复导致失败
Sqoop默认用主键做分片,如果表的主键是NULL,或者表压根没主键,会报No primary key found错误。解决办法就是用--split-by指定一个非空的唯一列。还有一个隐蔽坑:指定了--split-by,但这一列有NULL值,Sqoop生成的WHERE split_col >= MIN AND split_col < MAX条件会漏掉NULL值的数据,或者报类型转换错误。处理办法是导入前先把NULL值填掉,或者用boundary-query排除掉NULL区间。我踩过这个坑之后,养成了一个习惯:任何表导入前先跑一个SELECT COUNT(*) WHERE split_col IS NULL,检查一下。
7.4 数据倾斜的定位与处理
数据倾斜的现象很典型:一个Map任务跑很久,其他Map任务早早结束,整个作业的总耗时被这个“长尾任务”拖住。定位方法是在YARN页面上看每个Map任务的Rows,差距超过几倍就是倾斜。处理手段前面优化章节讲了,核心就是换分片列或者手动写boundary-query把大区间拆小。比如某个城市的订单量占了全表的80%,如果按city_id切分,某个分片就是巨无霸。这时候按order_id切分,或者把分片粒度写得细一点,均匀度会好很多。
7.5 中文乱码问题
导出来的中文变成???或者乱码,九成是字符集没配对。MySQL这边表是utf8mb4,JDBC连接串必须加characterEncoding=utf8,Sqoop这边读到的字节才会正确解码。我在5.1节里写的连接串已经带了这个参数,你直接照抄就行。还有一种情况是MySQL库和表字符集不一致,表是utf8mb4,库是latin1,Sqoop根据库的字符集解码,照样乱。建议导入前先SHOW CREATE TABLE user_info\G看看表的字符集定义,统一成utf8mb4最稳妥。
7.6 常见错误速查表
| 报错信息 | 原因 | 解决办法 |
|---|---|---|
| Connection refused | MySQL没起来或端口不通 | 检查MySQL服务、端口、防火墙 |
| No primary key found | 表无主键 | 加--split-by指定分片列 |
| Target directory already exists | HDFS目标目录已存在 | 换目录或加--append |
| SSL connection error | MySQL 8 SSL握手失败 | 连接串加useSSL=false |
| ClassNotFoundException | JDBC驱动缺失或版本不对 | 放驱动jar到$SQOOP_HOME/lib |
| Failed on local exception | 认证插件不兼容 | 换新版驱动或改mysql_native_password |
| java.sql.SQLException: null | NULL值处理不当 | 用--null-string处理空值 |
8. 最后分享几个我压箱底的小技巧
跑Sqoop这些年,我总结了几条可能文档里不会写的东西。第一条:Sqoop导入对时间类型有坑,MySQL的datetime到HDFS文本格式会带.0后缀,比如2024-05-20 10:30:00.0,Hive里用string类型接还好,如果用timestamp类型就得做转换。我一般建议落地用string,后续在ETL里自己控制格式。第二条:小表不要开高并行度,导几百行的表,-m设1就够了,省得为这点数据起一堆Container浪费资源。第三条:别把--target-dir放到根目录下,最好按/data/ods/表名这种分层方式组织,后续做分区、权限管理都方便。
根据我个人的体会,Sqoop用得好不好,一半在参数配置,一半在数据认知。参数再多,不理解分片原理,遇到问题还是抓瞎。真正学会Sqoop的标志不是背会了多少参数,而是你拿到一张表,能一眼判断出该用什么分片列、设多少并行度、走全量还是增量、要不要压缩,这才叫入门了。这套东西虽然老,但基础打得牢,后面接Flink、接DataX都会很有帮助。希望这篇文章能帮你把Sqoop这条路走稳,少踩几个我当年踩过的坑。