作者:IvanCodes
日期:2025年6月7日
专栏:Sqoop教程
Apache Sqoop 提供了作业 (Job) 的概念,它允许用户保存和重用复杂的 Sqoop 命令(包括导入或导出的所有参数)。这对于定期执行的、参数固定的数据迁移任务非常有用。此外,在自动化脚本中执行 Sqoop 作业时,处理密码是一个关键的安全问题,我们将探讨免密执行的几种方法。
思维导图
一、Sqoop Job 基础
什么是 Sqoop Job?
一个 Sqoop Job 是一个已保存的 Sqoop 命令的命名实例。它会记录下你执行 sqoop import
或 sqoop export
时使用的所有选项,包括连接字符串、用户名、表名、目标目录、增量导入参数等。
Sqoop Job 的优势:
- 简化重复操作 :只需创建一次作业,后续通过作业名即可执行,无需重复输入冗长的命令。
- 参数持久化 :作业保存了所有配置参数。
- 增量导入状态管理 :对于增量导入,Sqoop Job 会自动记录和更新
--last-value
(上次成功导入的检查点),使得后续增量导入可以无缝衔接。 - 易于集成到调度系统 :可以方便地被 Oozie、Azkaban、Airflow 或 Cron 等调度工具调用。
Sqoop Metastore:
Sqoop Job 的元数据 (作业定义、上次执行状态等) 存储在 Sqoop Metastore 中。
- 默认 Metastore :是一个本地的、基于文件的 HSQLDB 数据库,通常位于用户主目录下的
.sqoop/
目录或 Sqoop 安装目录下的metastore
子目录。这种方式只适用于单用户或测试环境。 - 共享 Metastore :在生产环境或多用户环境中,强烈建议配置 Sqoop 使用外部的关系型数据库 (如 MySQL, PostgreSQL) 作为共享 Metastore。这需要在
sqoop-site.xml
(或旧版本的sqoop-env.sh
) 中进行配置。
二、Sqoop Job 常用命令
- 创建作业 (
sqoop job --create
)
bash
sqoop job --create <job-name> -- <tool-name> [tool-arguments]
-
<job-name>
: 你给作业起的名字。 -
--
: 非常重要的分隔符,用于区分sqoop job
命令本身的选项和被保存的 Sqoop 工具命令 (如import
或export
) 及其参数。 -
<tool-name>
:import
或export
。 -
[tool-arguments]
: 对应import
或export
命令的所有参数。 -
执行作业 (
sqoop job --exec
)
bash
sqoop job --exec <job-name> [override-options]
-
执行已创建的作业。
-
[override-options]
: (可选) 可以在执行时临时覆盖作业中已保存的某些参数 (例如--password
,或--target-dir
用于不同的输出)。 -
查看作业定义 (
sqoop job --show
)
bash
sqoop job --show <job-name>
-
显示作业保存的所有参数和当前状态 (如增量导入的
last-value
)。 -
列出所有作业 (
sqoop job --list
)
bash
sqoop job --list
- 删除作业 (
sqoop job --delete
)
bash
sqoop job --delete <job-name>
三、Sqoop Job 代码案例 (以增量导入为例)
假设我们需要定期将MySQL表 mydb.user_activity
(包含 id
和 last_login_ts
列) 的增量数据导入到HDFS。
1. 创建一个增量导入作业 (append 模式):
bash
sqoop job --create daily_user_activity_append -- import \
--connect jdbc:mysql://mysql.example.com:3306/mydb \
--username db_user \
--P \
--table user_activity \
--target-dir /data/user_activity/append_mode \
--incremental append \
--check-column id \
--m 1
--P
: 提示交互式输入密码。创建作业时不应将密码硬编码到作业定义中。密码会在首次执行或显式提供时被Sqoop Metastore (如果配置了安全存储) 安全地记住 (或者依赖后续的免密配置)。
2. 执行作业:
bash
sqoop job --exec daily_user_activity_append
- Sqoop会自动使用上次成功执行后保存的
id
的最大值作为本次导入的--last-value
。
3. 创建一个增量导入作业 (lastmodified 模式):
bash
sqoop job --create hourly_user_activity_lastmod -- import \
--connect jdbc:mysql://mysql.example.com:3306/mydb \
--username db_user \
--P \
--table user_activity --target-dir /data/user_activity/lastmod_mode \
--incremental lastmodified \
--check-column id \
--last-modified-column last_login_ts \
--m 1
4. 执行作业并覆盖密码 (如果需要):
bash
sqoop job --exec hourly_user_activity_lastmod \
--password 'yourSecretPassword'
- 一般不推荐在命令行直接提供密码,这里仅为演示覆盖选项。
四、Sqoop 免密执行
在自动化脚本或调度系统中执行Sqoop作业时,避免在命令或脚本中硬编码密码至关重要。
方法一:使用密码文件 (--password-file
)
你可以将密码存储在一个文件中,并限制该文件的访问权限。
- 创建密码文件 (例如,在
hadoop01
上):
bash
echo "yourSecretPassword" > /home/hadoop_user/.sqoop_db_password.txt
chmod 400 /home/hadoop_user/.sqoop_db_password.txt
- 在Sqoop命令或作业定义中使用
--password-file
:- 直接执行时:
bash
sqoop import --connect jdbc:mysql://mysql.example.com:3306/mydb \
--username db_user \
--password-file file:///home/hadoop_user/.sqoop_db_password.txt \
--table user_activity \
--target-dir /data/some_dir \
--m 1
(注意 file:///
前缀,表示本地文件系统路径)
- 创建作业时 (更推荐,密码文件路径会被保存到作业定义中):
bash
sqoop job --create my_secure_import_job -- import \
--connect jdbc:mysql://mysql.example.com:3306/mydb \
--username db_user \
--password-file file:///home/hadoop_user/.sqoop_db_password.txt \
--table user_activity --target-dir /data/some_dir \
--m 1
之后执行 sqoop job --exec my_secure_import_job
时,Sqoop会自动读取密码文件。
方法二:使用 Hadoop Credential Provider API (更安全)
Hadoop提供了一个凭证提供者API,可以将敏感信息 (如密码) 存储在加密的凭证文件 (keystore) 中。Sqoop可以集成这个API。
- 创建凭证文件并存储密码别名 :
(需要在Hadoop环境中执行hadoop credential
命令)
bash
hadoop credential create mysql.db.password -provider jceks://hdfs/user/hadoop_user/credentials/sqoop.jceks
执行上述命令后,会提示你输入密码。密码会以 mysql.db.password
(别名) 存储在 HDFS 上的 sqoop.jceks
文件中 (路径可以自定义)。
- 配置Sqoop以使用Credential Provider :
修改sqoop-site.xml
(位于Sqoop的conf
目录),添加以下属性:
xml
<property>
<name>sqoop.metastore.client.record.password</name>
<value>false</value>
<description>If true, Sqoop will record passwords in the metastore.
Set to false if using a password-file or credential provider.</description>
</property>
<property>
<name>hadoop.security.credential.provider.path</name>
<value>jceks://hdfs/user/hadoop_user/credentials/sqoop.jceks</value>
<description>Path to the Hadoop credential provider keystore.</description>
</property>
-
在Sqoop命令或作业中使用密码别名 :
不需要再提供
--password
或--password-file
。Sqoop会自动尝试从配置的Credential Provider中查找与连接相关的密码别名 (Sqoop会基于JDBC URL和用户名构造一个预期的别名,或者你可以显式指定,但这不常见于--password
选项)。更常见的是,如果连接参数和用户与凭证别名能某种方式对应 (具体机制可能依赖Sqoop版本和配置细节),Sqoop在需要密码时会自动查询。
更稳妥的做法是让Sqoop在创建作业时交互式提示密码 (
--P
),如果配置了Credential Provider,Sqoop可能会将此密码存入配置的provider中,并与该作业关联。这样,后续执行作业时就无需再次提供密码。
方法三:使用 --password-alias
(如果JDBC驱动和Sqoop版本支持)
某些JDBC驱动和较新版本的Sqoop可能支持直接通过JDBC URL或特定选项引用存储在外部密码库中的密码别名。这依赖具体实现。
五、重要安全提示
- 切勿在脚本或版本控制中硬编码密码。
- 使用权限最小化原则为Sqoop数据库用户授权。
- 定期审计和轮换密码及凭证。
- 保护好密码文件或凭证库文件的访问权限。
总结: Sqoop Job 极大简化了重复的数据迁移任务,特别是对于增量导入。结合密码文件或Hadoop Credential Provider,可以安全地实现自动化、免密的Sqoop作业执行。
练习题
背景:
你有一个MySQL数据库 sales_db
,其中有一个表 daily_sales
(id INT AUTO_INCREMENT PRIMARY KEY, product_name VARCHAR(100), quantity_sold INT, sale_date DATE, last_update TIMESTAMP)。你需要定期将此表的新增和更新数据导入到HDFS目录 /data/sales_reports_hdfs
。
题目:
- 创建增量导入作业 :创建一个名为
import_daily_sales_lastmod
的Sqoop作业,该作业使用lastmodified
模式,以id
为检查列,last_update
为最后修改时间列,将数据导入到HDFS的/data/sales_reports_hdfs
目录。首次执行时,你需要交互式输入数据库密码。 - 执行作业 :写出执行上题创建的
import_daily_sales_lastmod
作业的命令。 - 创建密码文件 :假设你的数据库密码是
MyS@l3sP@ss!
。在你的用户主目录下创建一个名为.sales_db_pwd.txt
的密码文件,并设置其权限为只有属主可读。 - 修改作业以使用密码文件 :Sqoop Job创建后,其核心参数是固定的。如果想让已创建的
import_daily_sales_lastmod
作业使用上一步创建的密码文件,你通常需要怎么做?(提示:考虑作业的可修改性或执行时覆盖,但最佳实践是什么?) - 思考题: 如果你配置了Hadoop Credential Provider,并且在创建Sqoop作业时使用了
--P
交互式输入了密码,Sqoop Metastore (特别是共享的、配置良好的Metastore) 通常会如何处理这个密码以便后续作业执行时不再需要输入?
答案:
- 创建增量导入作业:
bash
sqoop job --create import_daily_sales_lastmod -- import \
--connect jdbc:mysql://your_mysql_host:3306/sales_db \
--username sales_user \
--P \
--table daily_sales \
--target-dir /data/sales_reports_hdfs \
--incremental lastmodified \
--check-column id \
--last-modified-column last_update \
--m 1
- 执行作业:
bash
sqoop job --exec import_daily_sales_lastmod
- 创建密码文件:
bash
echo "MyS@l3sP@ss!" > ~/.sales_db_pwd.txt
chmod 400 ~/.sales_db_pwd.txt
- 修改作业以使用密码文件:
Sqoop作业一旦创建,其核心参数 (如连接串、表名、增量模式等) 通常不能直接修改。你不能简单地 "alter" 一个作业来添加或更改--password-file
选项。
最佳实践是删除旧作业,然后用包含--password-file
的新定义重新创建作业:
bash
sqoop job --delete import_daily_sales_lastmod
sqoop job --create import_daily_sales_lastmod -- import \
--connect jdbc:mysql://your_mysql_host:3306/sales_db \
--username sales_user \
--password-file file:///home/your_user_name/.sales_db_pwd.txt \
--table daily_sales \
--target-dir /data/sales_reports_hdfs \
--incremental lastmodified \
--check-column id \
--last-modified-column last_update \
--m 1
- 思考题答案:
如果配置了Hadoop Credential Provider,并且在创建Sqoop作业时使用了--P
交互式输入了密码,Sqoop (特别是当连接到配置良好的共享Metastore时,该Metastore自身也可能与Credential Provider集成或有安全存储机制) 可能会尝试将这个密码安全地存储起来。
- 一种可能是Sqoop Metastore本身对密码进行加密存储。
- 另一种更理想的情况是,Sqoop Metastore会与配置的 Hadoop Credential Provider 交互,将用户输入的密码以一个与该作业或连接相关的别名存储到Credential Provider的keystore中。
这样,当后续执行该作业时 (sqoop job --exec job_name
),Sqoop会自动从Metastore或通过Metastore间接从Credential Provider中检索所需的凭证 (密码),从而实现免密执行,用户无需再次输入密码。这个过程对用户是透明的。