未记录事实核验 · 状态:维护中
记录仅说明核验时间与维护状态,不保证适用于所有环境;使用前请留意正文中的版本、适用条件与风险提示。
warning 适用范围与待维护事项
本文适合作为 Impala 数据流转方案与练习清单,不是可连续运行的脚本。执行前须确认 Impala 及 Sqoop、对象存储连接器版本,准备文中未定义的表、目录和权限;不要在命令行填写真实密码。多处 INSERT OVERWRITE DIRECTORY 属于 Hive 风格示意,MERGE、Parquet 表 UPDATE 及部分控制表流程不能按当前写法直接在 Impala 运行。性能星级未经给定环境测试,分区字段、增量边界和实际导出方式均需另行设计验证。
数据导入导出 - Impala数据流转的桥梁
abstract 📋 本节概览
- 🎯 学习目标:掌握Impala中数据导入导出的各种方法和最佳实践
- 📚 前置知识:连接与子查询、HDFS基础操作
- ⏱️ 预计用时:90-120分钟
🤔 为什么数据导入导出很重要?
tip 💝 给数据ETL新手的温暖指导
🤗 学习数据导入导出就像学会搭建数据的"高速公路"!
如果你现在面对数据流转时想:
- "这么多种数据格式,该怎么处理?"
- "大量数据导入导出好慢,有没有快速方法?"
- "怎么保证数据在流转过程中不出错?"
别担心! 每个数据工程师都是从第一次数据迁移开始学习的 ✨
🚛 想象数据导入导出就像物流运输
物流运输的过程:
- 📦 数据源 = 货物仓库(各种格式的原始数据)
- 🚛 导入过程 = 运输车队(ETL工具和方法)
- 🏭 Impala表 = 加工厂(数据处理和分析中心)
- 📤 导出过程 = 配送系统(将结果送达目的地)
- 🛣️ 优化策略 = 选择最佳路线(提升传输效率)
掌握了数据流转技术,你就能让数据在系统间自由穿梭! 🌟
🎯 数据导入导出全景图
📊 数据流转架构
%%{init: {"theme":"base","themeVariables":{"fontFamily":"PingFang SC, -apple-system, Source Han Serif SC, serif","fontSize":"16px","background":"#FDFBF5","primaryColor":"#FDFBF5","primaryTextColor":"#2D2A26","primaryBorderColor":"#6E6A60","secondaryColor":"#E8EFE8","secondaryBorderColor":"#8FA890","secondaryTextColor":"#2D2A26","tertiaryColor":"#FDFBF5","tertiaryBorderColor":"#9A9488","tertiaryTextColor":"#6E6A60","lineColor":"#6E6A60","textColor":"#2D2A26","mainBkg":"#FDFBF5","nodeBorder":"#2D5A3D","clusterBkg":"#F3EEDF","clusterBorder":"#9A9488","edgeLabelBackground":"#FDFBF5"},"flowchart":{"htmlLabels":true,"curve":"basis","padding":20,"nodeSpacing":60,"rankSpacing":70,"useMaxWidth":false},"themeCSS":".node rect, .node polygon, .node circle { stroke-width: 1.5px; } .edgePath .path { stroke-width: 1.5px; } .cluster-label .nodeLabel, .cluster-label foreignObject { color: #2D2A26 !important; } .edgeLabel, .edgeLabel foreignObject { color: #2D2A26 !important; background-color: #FDFBF5; } .edgeLabel rect { fill: #FDFBF5; }"}}%%
flowchart TD
A[数据源] --> B[Impala数据导入]
B --> C[Impala数据处理]
C --> D[Impala数据导出]
D --> E[目标系统]
A --> A1[关系数据库<br/>MySQL, PostgreSQL]
A --> A2[文件系统<br/>CSV, JSON, Parquet]
A --> A3[云存储<br/>S3, OSS, GCS]
A --> A4[其他系统<br/>Hive, HBase]
B --> B1[LOAD DATA<br/>直接加载]
B --> B2[INSERT SELECT<br/>查询插入]
B --> B3[外部表<br/>External Table]
B --> B4[Sqoop导入<br/>批量导入]
D --> D1[文件导出<br/>INSERT OVERWRITE]
D --> D2[查询结果<br/>客户端保存]
D --> D3[Sqoop导出<br/>到关系数据库]
D --> D4[API导出<br/>程序接口]
E --> E1[数据仓库<br/>分析系统]
E --> E2[报表系统<br/>BI工具]
E --> E3[应用系统<br/>业务系统]
E --> E4[云平台<br/>云服务]
style A fill:#e1f5fe,stroke:#0277bd
style B fill:#f3e5f5,stroke:#7b1fa2
style C fill:#fff3e0,stroke:#ef6c00
style D fill:#e8f5e8,stroke:#2e7d32
style E fill:#fce4ec,stroke:#c2185b
🎯 导入导出方法对比
| 方法 | 数据量 | 性能 | 实施复杂度 | 适用场景 |
|---|---|---|---|---|
| LOAD DATA | 中小型 | ⭐⭐⭐ | ⭐⭐ | 文件直接加载 |
| INSERT SELECT | 小型 | ⭐⭐ | ⭐ | 简单数据迁移 |
| 外部表 | 大型 | ⭐⭐⭐⭐ | ⭐⭐⭐ | 零拷贝查询 |
| Sqoop | 大型 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 数据库批量同步 |
| INSERT OVERWRITE | 中大型 | ⭐⭐⭐⭐ | ⭐⭐ | 文件导出 |
📥 数据导入技术详解
📁 文件导入
1. 文本文件导入
-- 1.1 创建目标表
CREATE TABLE customer_import (
customer_id STRING,
customer_name STRING,
email STRING,
phone STRING,
registration_date DATE,
customer_type STRING
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ','
LINES TERMINATED BY '\n'
STORED AS TEXTFILE;
-- 1.2 准备CSV数据文件
-- 假设在HDFS路径 /data/import/customers.csv 有数据:
-- C001,张三公司,zhangsan@company.com,13800138001,2024-01-15,Enterprise
-- C002,李四贸易,lisi@trade.com,13800138002,2024-02-20,SMB
-- 1.3 使用LOAD DATA导入
LOAD DATA INPATH '/data/import/customers.csv'
INTO TABLE customer_import;
-- 1.4 验证导入结果
SELECT * FROM customer_import LIMIT 10;
SELECT COUNT(*) FROM customer_import;
2. 复杂格式文件导入
-- 2.1 JSON文件导入(通过外部表)
CREATE EXTERNAL TABLE json_logs_external (
log_line STRING
)
STORED AS TEXTFILE
LOCATION '/data/import/json_logs/';
-- 2.2 解析JSON数据
CREATE TABLE parsed_logs AS
SELECT
get_json_object(log_line, '$.timestamp') AS log_timestamp,
get_json_object(log_line, '$.user_id') AS user_id,
get_json_object(log_line, '$.action') AS action,
get_json_object(log_line, '$.ip_address') AS ip_address
FROM json_logs_external
WHERE log_line IS NOT NULL;
-- 2.3 管道符分隔文件处理
CREATE TABLE complex_format_data (
field1 STRING,
field2 STRING,
field3 STRING,
field4 STRING
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '|'
STORED AS TEXTFILE;
🗃️ Parquet文件导入
-- 3.1 创建Parquet表
CREATE TABLE sales_parquet_import (
order_id STRING,
customer_id STRING,
product_id STRING,
order_date DATE,
quantity INT,
unit_price DECIMAL(10,2),
total_amount DECIMAL(12,2)
)
STORED AS PARQUET;
-- 3.2 从现有Parquet文件导入
-- 方法1:直接指向Parquet文件的外部表
CREATE EXTERNAL TABLE sales_external_parquet (
order_id STRING,
customer_id STRING,
product_id STRING,
order_date DATE,
quantity INT,
unit_price DECIMAL(10,2),
total_amount DECIMAL(12,2)
)
STORED AS PARQUET
LOCATION '/data/parquet_files/sales/';
-- 方法2:从外部表导入到内部表
INSERT INTO sales_parquet_import
SELECT * FROM sales_external_parquet;
-- 3.3 分区Parquet文件导入
CREATE TABLE sales_partitioned_import (
order_id STRING,
customer_id STRING,
product_id STRING,
quantity INT,
unit_price DECIMAL(10,2),
total_amount DECIMAL(12,2)
)
PARTITIONED BY (
order_date DATE,
region STRING
)
STORED AS PARQUET;
-- 动态分区导入(须先在源数据中提供 region;上方 sales_external_parquet 未定义该列)
-- 以上为 Hive 的动态分区设置,Impala 不使用这两个配置项。
INSERT INTO sales_partitioned_import PARTITION (order_date, region)
SELECT
order_id,
customer_id,
product_id,
quantity,
unit_price,
total_amount,
order_date,
region
FROM sales_external_parquet;
🔗 从关系数据库导入
1. 使用Sqoop导入
# 1.1 基础Sqoop导入命令
sqoop import \
--connect jdbc:mysql://mysql-server:3306/ecommerce \
--username data_user \
--password 'password123' \
--table customers \
--target-dir /user/impala/external/customers \
--fields-terminated-by ',' \
--lines-terminated-by '\n' \
--null-string '\\N' \
--null-non-string '\\N'
# 1.2 增量导入
sqoop import \
--connect jdbc:mysql://mysql-server:3306/ecommerce \
--username data_user \
--password 'password123' \
--table orders \
--target-dir /user/impala/external/orders \
--incremental append \
--check-column order_id \
--last-value 1000 \
--fields-terminated-by ','
# 1.3 条件导入
sqoop import \
--connect jdbc:mysql://mysql-server:3306/ecommerce \
--username data_user \
--password 'password123' \
--table orders \
--where "order_date >= '2024-01-01'" \
--target-dir /user/impala/external/orders_2024 \
--fields-terminated-by ','
# 1.4 自定义SQL导入
sqoop import \
--connect jdbc:mysql://mysql-server:3306/ecommerce \
--username data_user \
--password 'password123' \
--query "SELECT o.order_id, o.customer_id, o.order_date, c.customer_name
FROM orders o JOIN customers c ON o.customer_id = c.customer_id
WHERE \$CONDITIONS" \
--target-dir /user/impala/external/order_customer_join \
--split-by o.order_id \
--fields-terminated-by ','
2. 在Impala中处理Sqoop导入的数据
-- 2.1 创建外部表指向Sqoop导入的数据
CREATE EXTERNAL TABLE sqoop_customers_external (
customer_id STRING,
customer_name STRING,
email STRING,
phone STRING,
registration_date STRING,
customer_type STRING
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ','
STORED AS TEXTFILE
LOCATION '/user/impala/external/customers';
-- 2.2 数据类型转换和清洗
CREATE TABLE customers_cleaned AS
SELECT
customer_id,
customer_name,
email,
phone,
CAST(registration_date AS DATE) AS registration_date,
customer_type
FROM sqoop_customers_external
WHERE customer_id IS NOT NULL
AND customer_name IS NOT NULL;
-- 2.3 增量数据合并
-- 假设有新的增量数据
MERGE customers_cleaned AS target
USING sqoop_customers_incremental AS source
ON target.customer_id = source.customer_id
WHEN MATCHED THEN
UPDATE SET
customer_name = source.customer_name,
email = source.email,
phone = source.phone
WHEN NOT MATCHED THEN
INSERT (customer_id, customer_name, email, phone, registration_date, customer_type)
VALUES (source.customer_id, source.customer_name, source.email, source.phone,
CAST(source.registration_date AS DATE), source.customer_type);
🌐 从云存储导入
1. Amazon S3导入
-- 1.1 创建指向S3的外部表
CREATE EXTERNAL TABLE s3_sales_data (
order_id STRING,
customer_id STRING,
product_id STRING,
order_date DATE,
quantity INT,
unit_price DECIMAL(10,2)
)
STORED AS PARQUET
LOCATION 's3a://my-data-bucket/sales-data/';
-- 1.2 从S3导入到本地表
CREATE TABLE local_sales_data AS
SELECT * FROM s3_sales_data
WHERE order_date >= '2024-01-01';
-- 1.3 分区导入
INSERT INTO local_sales_partitioned PARTITION (year, month)
SELECT
order_id,
customer_id,
product_id,
order_date,
quantity,
unit_price,
YEAR(order_date) AS year,
MONTH(order_date) AS month
FROM s3_sales_data
WHERE order_date >= '2024-01-01';
2. 阿里云OSS导入
-- 2.1 配置OSS访问(在集群配置中)
-- 需要在Hadoop配置中设置OSS访问密钥
-- 2.2 创建OSS外部表
CREATE EXTERNAL TABLE oss_log_data (
log_timestamp TIMESTAMP,
user_id STRING,
action STRING,
ip_address STRING,
user_agent STRING
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
STORED AS TEXTFILE
LOCATION 'oss://my-bucket/log-data/';
-- 2.3 数据清洗和导入
CREATE TABLE cleaned_log_data AS
SELECT
log_timestamp,
user_id,
action,
ip_address,
CASE
WHEN user_agent LIKE '%Mobile%' THEN 'Mobile'
WHEN user_agent LIKE '%Bot%' THEN 'Bot'
ELSE 'Desktop'
END AS device_type
FROM oss_log_data
WHERE log_timestamp IS NOT NULL
AND user_id IS NOT NULL;
📤 数据导出技术详解
📁 导出到文件系统
1. 基础文件导出
-- 1.1 导出为CSV文件
INSERT OVERWRITE DIRECTORY '/data/export/customer_summary'
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ','
LINES TERMINATED BY '\n'
SELECT
customer_id,
customer_name,
customer_type,
total_orders,
total_amount,
last_order_date
FROM (
SELECT
c.customer_id,
c.customer_name,
c.customer_type,
COUNT(o.order_id) AS total_orders,
SUM(o.order_amount) AS total_amount,
MAX(o.order_date) AS last_order_date
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.customer_name, c.customer_type
) customer_stats
ORDER BY total_amount DESC;
-- 1.2 导出为Parquet文件
INSERT OVERWRITE DIRECTORY '/data/export/sales_analysis'
STORED AS PARQUET
SELECT
product_id,
product_name,
category,
total_quantity_sold,
total_revenue,
avg_selling_price
FROM product_sales_summary
WHERE total_revenue > 10000;
2. 分区导出
-- 2.1 按分区导出
INSERT OVERWRITE DIRECTORY '/data/export/sales_by_region'
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ','
SELECT
region,
order_date,
customer_count,
total_sales,
avg_order_value
FROM regional_sales_summary
WHERE order_date >= '2024-01-01'
ORDER BY region, order_date;
-- 2.2 多格式导出
-- Parquet格式(用于后续分析)
INSERT OVERWRITE DIRECTORY '/data/export/analytics/parquet/'
STORED AS PARQUET
SELECT * FROM detailed_sales_analysis;
-- CSV格式(用于Excel分析)
INSERT OVERWRITE DIRECTORY '/data/export/analytics/csv/'
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ','
LINES TERMINATED BY '\n'
SELECT * FROM summary_sales_report;
🗃️ 导出到关系数据库
1. 使用Sqoop导出
# 1.1 基础导出到MySQL
sqoop export \
--connect jdbc:mysql://mysql-server:3306/reporting \
--username report_user \
--password 'password123' \
--table customer_summary \
--export-dir /data/export/customer_summary \
--fields-terminated-by ',' \
--input-null-string '\\N' \
--input-null-non-string '\\N'
# 1.2 更新模式导出
sqoop export \
--connect jdbc:mysql://mysql-server:3306/reporting \
--username report_user \
--password 'password123' \
--table sales_metrics \
--export-dir /data/export/sales_metrics \
--update-key order_date,region \
--update-mode updateonly \
--fields-terminated-by ','
# 1.3 自定义插入语句
sqoop export \
--connect jdbc:mysql://mysql-server:3306/reporting \
--username report_user \
--password 'password123' \
--table monthly_reports \
--columns "report_month,total_sales,customer_count,avg_order_value" \
--export-dir /data/export/monthly_summary \
--fields-terminated-by ','
2. 准备导出数据
-- 2.1 创建导出前的数据预处理
CREATE TABLE export_customer_summary AS
SELECT
customer_id,
customer_name,
CASE
WHEN customer_type = 'Enterprise' THEN 'ENT'
WHEN customer_type = 'SMB' THEN 'SMB'
ELSE 'OTHER'
END AS customer_type_code,
COALESCE(total_orders, 0) AS total_orders,
COALESCE(total_amount, 0.00) AS total_amount,
COALESCE(DATE_FORMAT(last_order_date, 'yyyy-MM-dd'), '1900-01-01') AS last_order_date_str
FROM customer_analysis_view;
-- 2.2 数据质量检查
SELECT
COUNT(*) AS total_records,
COUNT(CASE WHEN customer_id IS NULL THEN 1 END) AS null_customer_id,
COUNT(CASE WHEN customer_name IS NULL THEN 1 END) AS null_customer_name,
COUNT(CASE WHEN total_amount < 0 THEN 1 END) AS negative_amount
FROM export_customer_summary;
-- 2.3 导出前最终检查
SELECT * FROM export_customer_summary
WHERE customer_id IS NULL OR customer_name IS NULL
LIMIT 10;
🌐 导出到云存储
1. 导出到Amazon S3
-- 1.1 直接导出到S3
INSERT OVERWRITE DIRECTORY 's3a://my-export-bucket/customer-analysis/'
STORED AS PARQUET
SELECT
customer_id,
customer_name,
customer_segment,
lifetime_value,
last_purchase_date,
predicted_churn_risk
FROM customer_analytics_final
WHERE analysis_date = CURRENT_DATE();
-- 1.2 按日期分区导出到S3
INSERT OVERWRITE DIRECTORY 's3a://my-export-bucket/daily-sales/'
PARTITION BY (export_date STRING)
STORED AS PARQUET
SELECT
order_id,
customer_id,
product_id,
order_amount,
order_status,
DATE_FORMAT(CURRENT_DATE(), 'yyyy-MM-dd') AS export_date
FROM orders
WHERE order_date = CURRENT_DATE();
2. 导出到阿里云OSS
-- 2.1 导出分析结果到OSS
INSERT OVERWRITE DIRECTORY 'oss://analytics-bucket/monthly-reports/'
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
SELECT
report_month,
region,
total_sales,
total_orders,
unique_customers,
avg_order_value
FROM monthly_sales_report
WHERE report_month >= DATE_SUB(CURRENT_DATE(), 365);
-- 2.2 压缩导出
INSERT OVERWRITE DIRECTORY 'oss://analytics-bucket/compressed-data/'
STORED AS PARQUET
TBLPROPERTIES ('parquet.compression'='gzip')
SELECT * FROM large_dataset_summary;
🔄 ETL流程设计
📋 完整ETL流程示例
-- 1. 创建ETL控制表
CREATE TABLE etl_control (
process_name STRING,
last_run_date DATE,
last_run_status STRING,
records_processed BIGINT,
error_message STRING
) STORED AS PARQUET;
-- 2. ETL数据质量检查函数
CREATE VIEW data_quality_check AS
SELECT
'customers' AS table_name,
COUNT(*) AS total_records,
COUNT(CASE WHEN customer_id IS NULL THEN 1 END) AS null_keys,
COUNT(CASE WHEN customer_name IS NULL OR customer_name = '' THEN 1 END) AS missing_names,
COUNT(CASE WHEN email NOT RLIKE '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$' THEN 1 END) AS invalid_emails
FROM customers
UNION ALL
SELECT
'orders' AS table_name,
COUNT(*) AS total_records,
COUNT(CASE WHEN order_id IS NULL THEN 1 END) AS null_keys,
COUNT(CASE WHEN customer_id IS NULL THEN 1 END) AS missing_customer_id,
COUNT(CASE WHEN order_amount <= 0 THEN 1 END) AS invalid_amounts
FROM orders;
-- 3. 增量数据处理
WITH new_orders AS (
SELECT *
FROM external_orders_staging
WHERE order_date > (
SELECT COALESCE(MAX(last_run_date), DATE('1900-01-01'))
FROM etl_control
WHERE process_name = 'orders_incremental'
)
),
processed_orders AS (
SELECT
order_id,
customer_id,
product_id,
order_date,
CASE
WHEN order_amount IS NULL THEN 0
WHEN order_amount < 0 THEN 0
ELSE order_amount
END AS order_amount,
COALESCE(order_status, 'Unknown') AS order_status
FROM new_orders
WHERE order_id IS NOT NULL
)
INSERT INTO orders
SELECT * FROM processed_orders;
-- 4. 记录ETL控制信息(示意:应由调度程序在作业成功后写入实际处理条数)
INSERT INTO etl_control
SELECT
'orders_incremental' AS process_name,
CURRENT_DATE() AS last_run_date,
'SUCCESS' AS last_run_status,
CAST(NULL AS BIGINT) AS records_processed, -- 占位:上条语句的 CTE 在此不可见,须填入实际处理条数
NULL AS error_message;
🔧 性能优化策略
1. 批量操作优化
-- 1.1 批量插入优化
-- 设置适当的批量大小
-- Impala 没有通用的 BATCH_SIZE 查询选项;批量大小需在上游写入任务中控制。
SET SYNC_DDL = false; -- 不等待 DDL 元数据更新传播至所有 impalad;不表示 DDL 作业异步执行
-- 1.2 分批处理大量数据
WITH batch_data AS (
SELECT
*,
ROW_NUMBER() OVER (ORDER BY order_id) AS rn
FROM large_source_table
WHERE processing_flag = false
)
INSERT INTO target_table
SELECT
order_id,
customer_id,
order_amount,
order_date
FROM batch_data
WHERE rn BETWEEN 1 AND 100000; -- 处理前10万条
-- 1.3 并行分区处理
INSERT INTO target_partitioned_table PARTITION (year, month)
SELECT
order_id,
customer_id,
order_amount,
order_date,
YEAR(order_date) AS year,
MONTH(order_date) AS month
FROM source_table
WHERE YEAR(order_date) = 2024
AND MONTH(order_date) = 1; -- 按月并行处理
2. 内存和资源优化
-- 2.1 设置适当的内存限制
SET MEM_LIMIT = 8GB;
SET QUERY_TIMEOUT_S = 3600;
-- 2.2 使用流式处理减少内存使用
INSERT INTO summary_table
SELECT
customer_id,
COUNT(*) AS order_count,
SUM(order_amount) AS total_amount
FROM orders
WHERE order_date >= DATE_SUB(CURRENT_DATE(), 30)
GROUP BY customer_id; -- 避免使用ORDER BY除非必要
-- 2.3 分阶段处理复杂转换
-- 第一阶段:基础清洗
CREATE TABLE stage1_cleaned AS
SELECT
order_id,
TRIM(customer_id) AS customer_id,
CASE
WHEN order_amount IS NULL THEN 0
ELSE order_amount
END AS order_amount,
order_date
FROM raw_orders
WHERE order_id IS NOT NULL;
-- 第二阶段:业务逻辑处理
CREATE TABLE stage2_enriched AS
SELECT
s1.*,
c.customer_name,
c.customer_type
FROM stage1_cleaned s1
LEFT JOIN customers c ON s1.customer_id = c.customer_id;
-- 第三阶段:最终输出
CREATE TABLE final_orders AS
SELECT
order_id,
customer_id,
customer_name,
customer_type,
order_amount,
order_date,
CASE
WHEN customer_type = 'VIP' THEN order_amount * 0.95
ELSE order_amount
END AS final_amount
FROM stage2_enriched;
🔍 数据导入导出监控
📊 数据量和质量监控
-- 1. 创建数据质量监控表
CREATE TABLE data_quality_metrics (
check_date DATE,
table_name STRING,
metric_name STRING,
metric_value BIGINT,
threshold_value BIGINT,
status STRING
) STORED AS PARQUET;
-- 2. 数据量监控
INSERT INTO data_quality_metrics
SELECT
CURRENT_DATE() AS check_date,
'orders' AS table_name,
'record_count' AS metric_name,
COUNT(*) AS metric_value,
1000000 AS threshold_value, -- 预期最小记录数
CASE
WHEN COUNT(*) >= 1000000 THEN 'PASS'
ELSE 'FAIL'
END AS status
FROM orders
WHERE order_date = CURRENT_DATE();
-- 3. 数据完整性检查
WITH quality_checks AS (
SELECT
'customers' AS table_name,
'null_customer_id' AS metric_name,
COUNT(CASE WHEN customer_id IS NULL THEN 1 END) AS metric_value,
0 AS threshold_value
FROM customers
UNION ALL
SELECT
'orders' AS table_name,
'future_orders' AS metric_name,
COUNT(CASE WHEN order_date > CURRENT_DATE() THEN 1 END) AS metric_value,
0 AS threshold_value
FROM orders
UNION ALL
SELECT
'orders' AS table_name,
'negative_amounts' AS metric_name,
COUNT(CASE WHEN order_amount < 0 THEN 1 END) AS metric_value,
0 AS threshold_value
FROM orders
)
INSERT INTO data_quality_metrics
SELECT
CURRENT_DATE() AS check_date,
table_name,
metric_name,
metric_value,
threshold_value,
CASE
WHEN metric_value <= threshold_value THEN 'PASS'
ELSE 'FAIL'
END AS status
FROM quality_checks;
-- 4. 监控报告查询
SELECT
check_date,
table_name,
COUNT(*) AS total_checks,
COUNT(CASE WHEN status = 'PASS' THEN 1 END) AS passed_checks,
COUNT(CASE WHEN status = 'FAIL' THEN 1 END) AS failed_checks,
ROUND(COUNT(CASE WHEN status = 'PASS' THEN 1 END) * 100.0 / COUNT(*), 2) AS pass_rate
FROM data_quality_metrics
WHERE check_date >= DATE_SUB(CURRENT_DATE(), 7)
GROUP BY check_date, table_name
ORDER BY check_date DESC, table_name;
⏱️ 性能监控
-- 5. ETL性能监控
CREATE TABLE etl_performance_log (
job_name STRING,
start_time TIMESTAMP,
end_time TIMESTAMP,
duration_minutes INT,
records_processed BIGINT,
records_per_minute BIGINT,
status STRING,
error_message STRING
) STORED AS PARQUET;
-- 6. 记录ETL性能
-- 在ETL脚本开始时记录
INSERT INTO etl_performance_log VALUES
('daily_customer_import', NOW(), NULL, NULL, NULL, NULL, 'RUNNING', NULL);
-- 在ETL脚本结束时更新(以下 UPDATE 不适用于上方创建的 Parquet 表,须另选支持更新的表或写入方式)
UPDATE etl_performance_log
SET
end_time = NOW(),
duration_minutes = TIMESTAMPDIFF(MINUTE, start_time, NOW()),
records_processed = 150000,
records_per_minute = 150000 / TIMESTAMPDIFF(MINUTE, start_time, NOW()),
status = 'COMPLETED'
WHERE job_name = 'daily_customer_import'
AND status = 'RUNNING'
AND DATE(start_time) = CURRENT_DATE();
🎉 数据导入导出学习收获
success 🌟 **你已阅读数据流转的核心练习材料,技能掌握仍需通过实际运行与验证确认!**
这些材料涉及的ETL能力方向:
- ✅ 多源导入:文件、数据库、云存储等各种数据源
- ✅ 格式处理:CSV、JSON、Parquet等多种格式
- ✅ 批量操作:高效的大数据量导入导出
- ✅ 增量同步:智能的增量数据处理
- ✅ 质量控制:数据质量检查和监控
- ✅ 性能优化:ETL流程性能调优
- ✅ 云端集成:与云平台的无缝对接
这些练习有助于熟悉数据管道的部分技能;企业级管道仍需经过兼容性、质量和运维验证。 🚀
🎯 实战技能验证
完成以下ETL项目挑战来验证你的技能:
-- 🎯 挑战1:构建完整的客户数据ETL管道
-- 要求:从多个数据源整合客户信息,包含数据清洗、去重、质量检查
-- 步骤1:创建统一客户主表
CREATE TABLE customer_master (
customer_id STRING,
customer_name STRING,
email STRING,
phone STRING,
registration_date DATE,
customer_type STRING,
status STRING,
created_time TIMESTAMP,
updated_time TIMESTAMP,
data_source STRING
)
PARTITIONED BY (load_date DATE)
STORED AS PARQUET;
-- 步骤2:从CRM系统导入
INSERT INTO customer_master PARTITION (load_date)
SELECT
CONCAT('CRM_', customer_id) AS customer_id,
TRIM(UPPER(customer_name)) AS customer_name,
LOWER(TRIM(email)) AS email,
REGEXP_REPLACE(phone, '[^0-9]', '') AS phone,
registration_date,
customer_type,
CASE
WHEN status IN ('active', 'ACTIVE', '1') THEN 'Active'
WHEN status IN ('inactive', 'INACTIVE', '0') THEN 'Inactive'
ELSE 'Unknown'
END AS status,
NOW() AS created_time,
NOW() AS updated_time,
'CRM' AS data_source,
CURRENT_DATE() AS load_date
FROM crm_customers_staging
WHERE customer_id IS NOT NULL
AND customer_name IS NOT NULL;
-- 步骤3:数据去重和质量检查
WITH duplicate_check AS (
SELECT
customer_id,
email,
COUNT(*) AS dup_count,
MAX(updated_time) AS latest_update
FROM customer_master
WHERE load_date = CURRENT_DATE()
GROUP BY customer_id, email
HAVING COUNT(*) > 1
),
quality_summary AS (
SELECT
COUNT(*) AS total_records,
COUNT(CASE WHEN email NOT RLIKE '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$' THEN 1 END) AS invalid_emails,
COUNT(CASE WHEN LENGTH(phone) < 10 THEN 1 END) AS invalid_phones,
COUNT(CASE WHEN customer_name IS NULL OR TRIM(customer_name) = '' THEN 1 END) AS missing_names,
COUNT(CASE WHEN email RLIKE '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$'
AND LENGTH(phone) >= 10
AND customer_name IS NOT NULL AND TRIM(customer_name) <> ''
THEN 1 END) AS valid_records
FROM customer_master
WHERE load_date = CURRENT_DATE()
)
SELECT
'Data Quality Summary' AS report_type,
CURRENT_DATE() AS report_date,
total_records,
invalid_emails,
invalid_phones,
missing_names,
ROUND(valid_records * 100.0 / NULLIF(total_records, 0), 2) AS data_quality_score -- 满足全部示例规则的记录比例;空数据集返回 NULL
FROM quality_summary;
-- 🎯 挑战2:实时销售数据导出到多个目标系统
-- 要求:将Impala中的销售分析结果导出到文件、数据库和云存储
-- 创建销售分析视图
CREATE VIEW daily_sales_analysis AS
SELECT
order_date,
region,
product_category,
COUNT(DISTINCT customer_id) AS unique_customers,
COUNT(*) AS total_orders,
SUM(order_amount) AS total_sales,
AVG(order_amount) AS avg_order_value,
SUM(CASE WHEN customer_type = 'VIP' THEN order_amount ELSE 0 END) AS vip_sales
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
JOIN products p ON o.product_id = p.product_id
WHERE o.order_status = 'Completed'
AND o.order_date >= DATE_SUB(CURRENT_DATE(), 30)
GROUP BY order_date, region, product_category;
-- 导出到CSV(给业务团队)
INSERT OVERWRITE DIRECTORY '/export/business_reports/daily_sales_csv/'
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ','
LINES TERMINATED BY '\n'
SELECT
order_date,
region,
product_category,
unique_customers,
total_orders,
total_sales,
avg_order_value,
vip_sales
FROM daily_sales_analysis
ORDER BY order_date DESC, region, product_category;
-- 导出到Parquet(给数据科学团队)
INSERT OVERWRITE DIRECTORY '/export/analytics/daily_sales_parquet/'
STORED AS PARQUET
SELECT * FROM daily_sales_analysis;
-- 准备导出到数据库的格式化数据
CREATE TABLE db_export_sales_summary AS
SELECT
DATE_FORMAT(order_date, 'yyyy-MM-dd') AS order_date_str,
region,
product_category,
CAST(unique_customers AS STRING) AS unique_customers_str,
CAST(total_orders AS STRING) AS total_orders_str,
CAST(ROUND(total_sales, 2) AS STRING) AS total_sales_str,
CAST(ROUND(avg_order_value, 2) AS STRING) AS avg_order_value_str,
CAST(ROUND(vip_sales, 2) AS STRING) AS vip_sales_str,
DATE_FORMAT(NOW(), 'yyyy-MM-dd HH:mm:ss') AS export_timestamp
FROM daily_sales_analysis;
🎯 下一步学习建议
立即实践:
- [ ] 设计一个完整的ETL流程处理真实业务数据
- [ ] 实践增量数据同步和数据质量监控
- [ ] 优化大数据量的导入导出性能
继续学习:
quote 💭 **数据流转的艺术**
数据导入导出是数据工程的核心技能:
- 🌊 数据流动:让数据在系统间自由流转
- 🔍 质量保证:确保数据在流转过程中的完整性
- ⚡ 性能优化:在准确性和效率之间找到平衡
- 🔄 自动化运维:构建可靠的自动化数据管道
掌握ETL技能是参与企业数据流转建设的重要一步,仍需结合实际环境持续实践! ✨
创建时间:2024-12-11
温度指数:⭐⭐⭐⭐⭐ (实用性强,操作详细)
预计学习时长:90-120分钟
下一步学习:分区表 → 文件格式优化
#Impala #数据导入 #数据导出 #ETL #Sqoop #数据质量 #性能优化 #云端集成