摘要:Sqoop
,MySQL
,Hive
,Impala
在Spark跑批到Hive的任務后面加入Sqoop任務,將數據從Hive導入MySQL提供在線查詢服務,記錄一下Shell腳本,主要是Shell常用語法,Impala命令參數,Sqoop命令參數
導數需求
有一張Parquet格式的Hive分區表test.sqoop_test,用Impala查看表結構,其中dt是分區字段
[cloudera01:21000] > desc sqoop_test;
Query: describe sqoop_test
+---------------+--------+---------+
| name | type | comment |
+---------------+--------+---------+
| industry_code | string | |
| rank | string | |
| inc | string | |
| dt | string | |
+---------------+--------+---------+
需要導入MySQL庫中,每天導入HIve表中最新dt分區的數據,根據Industry_code覆蓋更新導入,其他兩個字段是MySQL JSON類型,MySQL使用的是8.0.25版本,默認區分大小寫,所以字段大小寫要一致
mysql> desc sqoop_test;
+---------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------+-------------+------+-----+---------+-------+
| industry_code | varchar(20) | NO | PRI | NULL | |
| rank | json | YES | | NULL | |
| inc | json | YES | | NULL | |
+---------------+-------------+------+-----+---------+-------+
3 rows in set (0.64 sec)
導出方式
先將數據從Hive表導出到HDFS,或者導入到一張新的Hive表,再從HDFS將數據導入到MySQL。雖然比直接導出多了一步操作,但是可以實現對數據的更精準的操作,主要體現在
- 在從Hive表導出到HDFS時,可以進一步對數據進行字段篩選、字段加工、數據過濾操作,從而使得HDFS上的數據更“接近”或等于將來實際要導入MySQL表的數據
- 在從HDFS導入MySQL時,也是將一個“小數據集”與目標表中的數據做對比,會提高導出速度
導數流程如下
導出到Hive中間表的方式
將原始Hive表sqoop_test進列篩選,行和分區過濾之后導入中間表sqoop_test_push,再導入MySQL,先建立一張Hive中間表,作為存儲一個指定dt版本的中間數據
hive> create table sqoop_test_push (
> `industry_code` string,
> `rank` string,
> `inc` string)
> stored as PARQUET;
下一步使用Impala同步元數據,并且使用Impala shell傳入本地腳本文件獲得Hive表的最大分區dt,shell腳本如下
sql="invalidate metadata test.sqoop_test; select max(dt) as cnt from test.sqoop_test"
max_partition=$(impala-shell -l --auth_creds_ok_in_clear -u cdh_dev --ldap_password_cmd="sh /home/etl/impala.sh" -i cloudera03 -d test -B -q "$sql")
max_partition變量被impala-shell執行的返回值賦值,impala-shell的參數包括
-
-l
:使用LDAP向Impala進行身份驗證。必須將Impala配置為允許LDAP身份驗證 auth_creds_ok_in_clear
-
--ldap_password_cmd
:啟動命令帶有檢索到的密碼,傳入一個獲得密碼的shell命令,比如--ldap_password_cmd="echo -n 123456" -
-i
:要連接的impala host和port,默認port是21000 -
-d
:設置數據庫 -
-B
:去除格式化,查詢大數據量時可以提高性能 -
-q
:執行一個query語句
執行完畢得到最大分區
[root@ubuntu ~]# echo $max_partition
20210317
下一步操作impala-shell將Hive表過濾分區和字段,寫入中間表,先指定一個INSERT OVERWRITE導入Hive表SQL語句文件,位置在/home/push_test.sql
use ${var:impala_database};
invalidate metadata ${var:impala_table_push};
insert overwrite table ${var:impala_table_push}
select industry_code,
rank,
inc
from ${var:impala_table} where dt='${var:max_partition}';
以上SQL語句先對創建的中間表做impala元數據同步,然后使用insert overwrite
以過濾后的原表數據直接覆蓋中間表,接下來使用impala-shell執行以上SQL語句文件
impala-shell -l --auth_creds_ok_in_clear \
-u cdh_dev \
--ldap_password_cmd="sh /home/etl/impala.sh" \
-i cloudera03 \
-d test \
-f "/home/push_test.sql" \
--var=max_partition="$max_partition" \
--var=impala_table="sqoop_test" \
--var=impala_table_push="sqoop_test_push" \
--var=impala_database="test"
其中
-
-f
:表示執行一個query文件,其中以分號;作為語句分隔條件 -
--var
:自定義impala上下文變量,可以多次使用,必須指定key=value的格式,在定義的時候${var:KEY}
,賦值的時候--var=KEY=VALUE
執行成功后中間表sqoop_test_push就有了需要導入MySQL的數據,并且和要求的最終數據是一致的
下一步使用sqoop進行導數,由于目標MySQL版本是8.0.025的,sqoop需要高版本的mysql-connect驅動,否則報錯無法建立鏈接,先在maven倉庫上下載高版本的驅動mysql-connector-java-8.0.25.jar
,放在sqoop的lib目錄下,然后啟動sqoop開始導數
導入方式為allowinsert
覆蓋插入,即無則插入,有則根據主鍵更新
sudo -u hdfs sqoop export \
--connect "jdbc:mysql://192.168.67.72:3306/test" \
--username "root" \
--password "123456" \
--table "sqoop_test" \
--update-mode allowinsert \
--update-key "industry_code" \
--hcatalog-database "test" \
--hcatalog-table "sqoop_test_push" \
--null-string '\\N' \
--null-non-string '\\N' \
-m 1
相關參數如下
-
export
:從hdfs導出數據到關系型數據庫 -
--connect
:指定jdbc連接字符串 -
--username
:數據庫用戶 -
--password
:數據庫密碼 -
--table
:導出的數據庫表名稱 -
--update-mode
:指定更新策略,包括updateonly
,allowinsert
,updateonly是默認模式,僅僅更新已存在的數據記錄,不會插入新紀錄,allowinsert有則更新,無則插入 -
--update-key
:更新參考的列名稱,多個列用逗號,隔開 -
--hcatalog-database
:hive數據庫,parquet格式的hive表使用hcatalog -
--hcatalog-table
:hive數據庫表名 -
--null-string
:針對string類型的字段,當Value是NULL,替換成指定的字符 -
--null-non-string
:針對非string類型的字段,當Value是NULL,替換成指定字符 -
-m
:并行化,使用n個map任務并行導出
再看MySQL已經成功導入20條數據
mysql> select count(*) from sqoop_test;
+----------+
| count(*) |
+----------+
| 20 |
+----------+
1 row in set (0.19 sec)
完整Shell腳本
完成的shell腳本如下,掛在Spark入庫Hive的作業后面執行
[root@ubuntu ~]# vim push_rank.sh
#!/bin/bash
mysql_url="jdbc:mysql://192.168.67.72/test"
mysql_username="root"
mysql_password="123456"
mysql_table="sqoop_test"
impala_username="cdh_dev"
impalad_host="cloudera03"
impala_database='test'
impala_table='sqoop_test'
impala_table_push='sqoop_test_push'
push_sql_path='/home/push_rank.sql'
sql="invalidate metadata $impala_database.$impala_table; select max(dt) as cnt from $impala_database.$impala_table"
max_partition=$(impala-shell -l --auth_creds_ok_in_clear -u $impala_username --ldap_password_cmd="sh /home/etl/impala.sh" -i $impalad_host -d test -B -q "$sql")
if [ $? -eq 0 ]; then
echo "Impala SQL執行成功!"
echo "最大分區為${max_partition}"
else
echo "Impala SQL執行失敗!"
exit 1
fi
impala-shell -l --auth_creds_ok_in_clear -u $impala_username --ldap_password_cmd="sh /home/etl/impala.sh" -i $impalad_host -d $impala_database -f $push_sql_path --var=max_partition="$max_partition" --var=impala_table=$impala_table --var=impala_table_push=$impala_table_push --var=impala_database=$impala_database
if [ $? -eq 0 ]; then
echo "impala執行成功,開始向mysql推數"
sudo -u hdfs sqoop export --connect ${mysql_url} --username ${mysql_username} --password ${mysql_password} --table ${mysql_table} --update-mode allowinsert --update-key "industry_code" --hcatalog-database ${impala_database} --hcatalog-table ${impala_table_push} --null-string '\\N' --null-non-string '\\N' -m 1;
if [ $? -eq 0 ]
then
echo "sqoop export success !"
else
echo "sqoop export failed !"
exit 1
fi
else
echo "impala執行失敗"
exit 1
fi
其中/home/push_rank.sql如下
use ${var:impala_database};
invalidate metadata ${var:impala_table_push};
insert overwrite table ${var:impala_table_push}
select industry_code,
rank,
inc
from ${var:impala_table} where dt='${var:max_partition}';