实时同步搞定之后,还剩一步:把 Oracle 里的历史数据(16.7 万条)批量导入 HBase。Sqoop 是现成的工具,按理说一条命令就能搞定。结果这一跑,又卡了很久。

现象

在 hadoop1 上执行 Sqoop import 命令:

sqoop import --append \
  --connect jdbc:oracle:thin:@92.16.16.61:1521:orcl \
  --username send --password send \
  --table TEST \
  --hbase-table FK:S02 \
  --hbase-row-key ID \
  --column-family CF \
  -m 1

命令提交后,MapReduce 作业开始运行,但一直卡在 map 0% 不动。几分钟后,控制台开始刷错误:

INFO zookeeper.ClientCnxn: Opening socket connection to server hadoop2/92.16.16.27:2181
WARN zookeeper.ClientCnxn: Session 0x0 for server null, unexpected error, closing socket connection
java.net.ConnectException: Connection refused

然后不断重试 hadoop2、hadoop3、hadoop1,循环往复。最终作业失败:

ERROR tool.ImportTool: Encountered IOException running import job: 
org.apache.hadoop.hbase.client.RetriesExhaustedException: Failed after attempts=36, 
exceptions: java.net.SocketTimeoutException: callTimeout=60000, callDuration=68386

排查思路

1.  确认 HBase 和 ZooKeeper 状态

用 jps 检查,HBase 进程都正常(HMaster、HRegionServer、HQuorumPeer 都在)。而且之前 GoldenGate 实时同步已经能写入数据,说明 HBase 本身是好的。

那为什么 Sqoop 连不上?仔细看日志,Sqoop 尝试连接 hadoop2:2181 和 hadoop3:2181,但只有 hadoop1 的 2181 端口是通的。之前为了简化,我把 hbase.zookeeper.quorum 改成了 hadoop1 单节点,但 Sqoop 似乎没有读取这个配置?它仍然从 HBase 的某个地方拿到了三节点的 quorum。

2. 检查 Sqoop 如何获取 ZooKeeper 地址

Sqoop 的 HBase 导入功能会从 hbase-site.xml 中读取 hbase.zookeeper.quorum。我确认 hadoop1 上的 /app/hadoop/hbase-1.2.1/conf/hbase-site.xml 中确实改成了 hadoop1。但 Sqoop 可能使用了其他路径下的配置文件(比如 /etc/hbase/conf)或者 classpath 中的旧配置。

于是我在 Sqoop 命令中显式指定 ZooKeeper 地址:

sqoop import ... --hbase-zookeeper-quorum hadoop1 --hbase-zookeeper-property-clientPort 2181

结果报错:Unrecognized argument: --hbase-zookeeper-quorum。查了一下,Sqoop 1.4.6 根本不支持这些参数(可能是更高版本才有)。这条路走不通。

3. 修改 HBase 配置并重启

既然 Sqoop 固执地要用三节点 quorum,那就让 hadoop2 和 hadoop3 上的 ZooKeeper 真正跑起来。

  • 在 hadoop2 和 hadoop3 上手动启动 ZooKeeper(HBase 内置的):

ssh hadoop2 '/app/hadoop/hbase-1.2.1/bin/hbase-daemon.sh start zookeeper'
ssh hadoop3 '/app/hadoop/hbase-1.2.1/bin/hbase-daemon.sh start zookeeper'
  • 检查端口:netstat -tlnp | grep 2181,三个节点都应该有监听。

  • 将 hbase-site.xml 中的 hbase.zookeeper.quorum 改回三节点:hadoop1,hadoop2,hadoop3

  • 重启 HBase。

再次运行 Sqoop 导入,这次没有 ZooKeeper 连接错误了,但作业仍然超时——这次是 HBase 写入超时。

4. 调整超时参数

在 hbase-site.xml 中增加:

<property>
  <name>hbase.client.operation.timeout</name>
  <value>120000</value>
</property>
<property>
  <name>hbase.rpc.timeout</name>
  <value>120000</value>
</property>

并加上 --batch 参数(虽然 Sqoop 1.4.6 不支持 --batch,但可以用 -D sqoop.hbase.bulk.load.enabled=false 之类的?不过最简单的还是增加 -m 1 并使用更小的批次)。

实际上,最终解决问题的是 将导入拆分成多个小批次,而不是一次导 16 万行。例如按 ID 范围分四次导入:

sqoop import ... --where "ID <= 50000" -m 1
sqoop import ... --where "ID > 50000 AND ID <= 100000" -m 1
...

但这样太麻烦。我尝试了另一个方向:将 HBase 的 ZooKeeper session 超时调大,并增加客户端的重试次数。同时确保 MapReduce 任务的并行度不要太高(-m 1 已经是最低)。

经过多次调整,最终一次导入成功,日志显示:

INFO mapreduce.ImportJobBase: Retrieved 167783 records.

根本原因

  • Sqoop 1.4.6 对 HBase 的集成不够智能,无法自动适应单节点 ZooKeeper 配置,必须要有完整的 ZooKeeper 集群。

  • HBase 默认的 RPC 超时较短(60 秒),导入大量数据时容易超时。

  • 网络或防火墙可能导致 ZooKeeper 节点间通信延迟。

方案总结

  1. 确保所有节点的 ZooKeeper 都正常启动,并开放 2181 端口。

  2. 调整 HBase 超时参数hbase.client.operation.timeouthbase.rpc.timeout)。

  3. 使用 -m 1 降低并行度,避免并发写入冲突。

  4. 如果仍然超时,可以拆分成多个小范围导入(按 ID 或分区键)。

历史数据导入成功后,HBase 表里终于有了 16.7 万行数据。加上实时同步的增量,Hive 查询时总数接近 16.8 万。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐