sqoop是apache旗下,用於關系型數據庫和hadoop之間傳輸數據的工具,sqoop可以用在離線分析中,將保存在mysql的業務數據傳輸到hive數倉,數倉分析完得到結果,再通過sqoop傳輸到mysql,最后通過web+echart來進行圖表展示,更加直觀的展示數據指標。
sqoop基礎
基本概念
如下圖所示,sqoop中有導入和導出的概念,參照物都是hadoop文件系統,其中關系型數據庫可以是mysql、oracle和db2,hadoop文件系統中可以是hdfs、hive和hbase等。執行sqoop導入和導出,其本質都是轉化成了mr任務去執行。
基本架構
目前sqoop提供了兩個版本,1.4.x的為sqoop1,1.99x的為sqoop2,前者因為安裝簡單,得到了大量使用,后者雖然引進了安全機制、web ui,rest api等更加方便使用的特性,但是安裝過程繁瑣暫時不記錄。
以下是sqoop1的結構圖,它只提供一個sqoop客戶端,使用命令行方式來執行導入/導出任務,最終任務都會被轉化為mr,實現數據在hdfs/hbase/hive和rdbms/企業數據倉庫之間的轉換。
sqoop安裝
sqoop的安裝相對簡單,只需選擇對應的sqoop解壓到安裝目錄即可,一般將sqoop安裝到已經安裝了mysql和hive的節點上。這里mysql版本為5.7.28,hive版本為cdh的1.1.0。
(1)解壓sqoop安裝包,sqoop版本為1.4.6。
[root@node01 /kkb/soft]# tar -zxvf sqoop-1.4.6-cdh5.14.2.tar.gz -C /kkb/install/
(2)修改sqoop根目錄/conf下的sqoop-env.sh文件,配置環境相關的參數。剛安裝后需要復制模版文件,命名為sqoop-env.sh,因為此次sqoop安裝后要實現關系型數據庫跟hadoop、hive和hbase的數據傳輸,因此在里面配置hadoop、hive和hbase的安裝路徑。
# Set Hadoop-specific environment variables here. #Set path to where bin/hadoop is available export HADOOP_COMMON_HOME=/kkb/install/hadoop-2.6.0-cdh5.14.2 #Set path to where hadoop-*-core.jar is available export HADOOP_MAPRED_HOME=/kkb/install/hadoop-2.6.0-cdh5.14.2 #set the path to where bin/hbase is available export HBASE_HOME=/kkb/install/hbase-1.2.0-cdh5.14.2 #Set the path to where bin/hive is available export HIVE_HOME=/kkb/install/hive-1.1.0-cdh5.14.2
(3)復制數據庫驅動包到sqoop的lib目錄,如圖拷貝mysql-connector-java-5.1.38.jar和java-json.jar。
(4)配置sqoop環境變量,也可以不配置,直接進入sqoop的bin目錄下執行sqoop腳本也行。
sqoop使用
接下來使用安裝的sqoop,實現sqoop導入和導出,還可以創建sqoop job來完成作業,另外記錄。
下面可以使用sqoop來獲取數據庫的信息。
# list-databases獲取mysql中數據庫,list-tables可以查看某個數據庫下的表
[root@node01 /kkb/install/sqoop-1.4.6-cdh5.14.2/lib]# sqoop list-databases --connect jdbc:mysql://node01:3306 --username root --password 123456
導入數據到hdfs
(1)不指定導出目錄和分隔符。
mysql中提前准備好數據,測試導入到hdfs。使用dbeaver工具,在mysql中創建數據庫sqooptest,並建表Person,數據如下。
id|name |age|score|position| --|------|---|-----|--------| 1|messi | 32| 55|前鋒 | 2|herry | 40| 30|前鋒 | 3|clyang| 33| 3|中場 | 4|ronald| 35| 45|左前鋒 |
使用如下命令,將Person表中的數據import到hdfs。
# import 導入
# --connect 指定mysql連接地址,數據庫為sqooptest
# --username mysql用戶名
# --password mysql密碼
# --table 指定要導出的表Person
# --m 指定map task數,默認是4個
[hadoop@node01 ~]$ sqoop import --connect jdbc:mysql://node01:3306/sqooptest --username root --password 123456 --table Person --m 1
導出后提示保存了4條記錄,即剛才mysql中的四條數據。
導出到hdfs后,默認保存位置為/user/hadoop/數據庫表名,如下圖所示。查看導出的內容,發現跟mysql中的一致,並且字段值之間使用逗號隔開。
(2)指定導出目錄和分隔符,mysql中數據依然使用上面的,另外終端執行sqoop命令時可以使用反斜杠'\'轉義字符來隔開各個參數,類似終端中使用scala的豎線'|'。
[hadoop@node01 ~]$ sqoop import \ > --connect jdbc:mysql://node01:3306/sqooptest \ > --username root \ > --password 123456 \ > --table Person \ > --target-dir /sqoop/person \ # 指定導出目錄 > --delete-target-dir \ # 如果導出目錄存在,就先刪除 > --fields-terminated-by '\t' \ # 指定字段數據分隔符 > --m 1
導出后,進入指定目錄查看,發現成功導出到指定目錄,並用制表符分隔開。
(3)導入表的數據子集,可以通過指定where參數,將符合條件的子集導入到hdfs。
[hadoop@node01 ~]$ sqoop import \ > --connect jdbc:mysql://node01:3306/sqooptest \ > --username root --password 123456 \ > --table Person \ > --target-dir /sqoop/person_where \ > --delete-target-dir \ > --where "name = 'messi'" \ # where指定條件 > --m 1
查看hdfs上數據,結果ok,只導出了name=messi的數據。
(4)可以使用--query,指定sql查詢條件過濾數據,再導入到hdfs。
[hadoop@node01 ~]$ sqoop import \ > --connect jdbc:mysql://node01:3306/sqooptest \ > --username root \ > --password 123456 \ > --target-dir /sqoop/person_sql \ > --delete-target-dir \ > --query 'select * from Person where age>20 and $CONDITIONS' \ # 指定查詢條件,並添加$CONDITIONS變量 > --m 2
執行后,發現報錯,提示並行import時,需要指定split-by的字段。
重新指定split-by的字段為表的id字段,再次執行,ok。
[hadoop@node01 ~]$ sqoop import \ > --connect jdbc:mysql://node01:3306/sqooptest \ > --username root \ > --password 123456 \ > --target-dir /sqoop/person_sql \ > --delete-target-dir \ > --query 'select * from Person where age>20 and $CONDITIONS' \ > --split-by 'id' \ > --m 2
查看hdfs,產生兩個maptask文件,可以看出是根據id分組后並行執行的結果。
(5)增量導入,有時候不需要導入表中的全部數據,只需要導入部分數據就可以。如增加行,就導入(append模式),或者某行時間戳有變化,就導入(lastmodified模式)。
append模式:
sqoop命令。
[hadoop@node01 ~]$ sqoop import \ > --connect jdbc:mysql://node01:3306/sqooptest \ > --username root \ > --password 123456 \ > --table Person \ > --incremental append \ # append模式 > --check-column id \ # 檢查列為id列 > --last-value 4 \ # id列上一個記錄的值為4 > --target-dir /sqoop/increment \ > --m 1
mysql中添加一行數據,id為5,添加后執行上面的命令。
id|name |age|score|position| --|------|---|-----|--------| 1|messi | 32| 55|前鋒 | 2|herry | 40| 30|前鋒 | 3|clyang| 33| 3|中場 | 4|ronald| 35| 45|左前鋒 | 5|kaka | 45| 2|右前鋒 |
執行沒有報錯,查看hdfs中內容,發現只導入了新增id為5的這行數據。
lastmodified模式:
這個模式是基於時間列的增量數據導入,mysql中新准備一張包含時間列的表和數據,如下所示。
id|name |salary|time | --|------|------|-------------------| 1 |clyang| 12000|2020-01-25 10:00:00| 2 |messi | 23000|2020-01-25 10:30:00| 3 |ronald| 22000|2020-01-25 10:45:00| 4 |herry | 21000|2020-01-25 11:15:00|
sqoop導入命令,使用lastmodified模式來導入新增數據。
[hadoop@node01 ~]$ sqoop import \ > --connect jdbc:mysql://node01:3306/sqooptest \ > --username root \ > --password 123456 \ > --table Man \ > --incremental lastmodified \ # lasmodified模式 > --check column time \ # 檢查列為時間列 > --last-value '2020-01-25 10:00:00' \ # 指定上一個時間點 > --target-dir /sqoop/increment1 \ > --m 1
執行完成后,時間點在10點以后的數據,都導入到了hdfs。
現在對表的數據進行修改,新增並修改數據,測試能否導入。
# 修改id=4的salary為8888,並新增一列id=5
id|name |salary|time | --|------|------|-------------------| 1 |clyang| 12000|2020-01-25 10:00:00| 2 |messi | 23000|2020-01-25 10:30:00| 3 |ronald| 22000|2020-01-25 10:45:00| 4 |herry | 8888|2020-01-25 11:15:00| 5 |kaka | 6666|2020-01-25 11:45:00|
sqoop命令,修改時間執行。
[hadoop@node01 ~]$ sqoop import \ > --connect jdbc:mysql://node01:3306/sqooptest \ > --username root \ > --password 123456 \ > --table Man \ > --incremental lastmodified \ # lasmodified模式 > --check column time \ # 檢查列為時間列 > --last-value '2020-01-25 10:30:00' \ # 指定上一個時間點 > --target-dir /sqoop/increment1 \ > --m 1
執行后報錯,提示需要添加--append或-merge-key,因為導出目錄已經存在了。
20/02/06 11:16:34 ERROR tool.ImportTool: Import failed: --merge-key or --append is required when using --incremental lastmodified and the output directory exists.
命名添加--append后執行沒有報錯,查看目錄下內容。
繼續修改數據測試。
# 修改id為5的數據,並添加id為6的數據
id|name |salary|time | --|-------|------|-------------------| 1 |clyang | 12000|2020-01-25 10:00:00| 2 |messi | 23000|2020-01-25 10:30:00| 3 |ronald | 22000|2020-01-25 10:45:00| 4 |herry | 8888|2020-01-25 11:15:00| 5 |kaka | 9999|2020-01-25 11:50:00| 6 |beckham| 23000|2020-01-25 12:00:00|
測試--merge-key的使用,sqoop命令行最后添加--merge-key id后執行沒有報錯。
[hadoop@node01 ~]$ sqoop import \ > --connect jdbc:mysql://node01:3306/sqooptest \ > --username root \ > --password 123456 \ > --table Man \ > --incremental lastmodified \ # lasmodified模式 > --check column time \ # 檢查列為時間列 > --last-value '2020-01-25 10:30:00' \ # 指定上一個時間點 > --target-dir /sqoop/increment1 \ > --m 1 > --merge-key id
目錄下內容發現只有一個文件,並且sqoop里導出的時間在10:30以后,但依然有10點的數據在里面,說明經歷了reduce階段進行合並。
導入數據到hive
導出數據到hive前,需要將hive中的一個包(hive-exec-1.1.0-cdh5.14.2.jar)拷貝到sqoop的lib目錄。
[hadoop@node01 /kkb/install/hive-1.1.0-cdh5.14.2/lib]$ cp hive-exec-1.1.0-cdh5.14.2.jar /kkb/install/sqoop-1.4.6-cdh5.14.2/lib/
(1)手動創建hive表后導入
先手動在hive中建一個接收數據的表,這里指定的分隔符和sqoop導出時的分隔符要一致。
# 創建數據庫 hive (default)> create database sqooptohive; OK Time taken: 0.185 seconds hive (default)> use sqooptohive; OK Time taken: 0.044 seconds # 創建表 hive (sqooptohive)> create external table person(id int,name string,age int,score int,position string)row format delimited fields terminated by '\t'; OK Time taken: 0.263 seconds hive (sqooptohive)> show tables; OK tab_name person
sqoop導出數據到hive表中。
[hadoop@node01 ~]$ sqoop import \ > --connect jdbc:mysql://node01:3306/sqooptest \ > --username root \ > --password 123456 \ > --table Person \ > --fields-terminated-by '\t' \ # 這里需要和hive中分隔指定的一樣 > --delete-target-dir \ > --hive-import \ # 導入hive > --hive-table sqooptohive.person \ #hive表 > --hive-overwrite \ # 覆蓋hive表中已有數據 > --m 1
查看hive表數據,發現導入ok。
(2)導入時自動創建hive表
也可以不需要提前創建hive表,會自動創建。
[hadoop@node01 ~]$ sqoop import \ > --connect jdbc:mysql://node01:3306/sqooptest \ > --username root \ > --password 123456 \ > --table Person \ > --hive-import \ > --hive-database sqooptohive \ > --hive-table person1 \ > --m 1
導入后,發現數據庫下多了一個表person1,查看數據ok。
導入數據到hbase
也可以將數據導入到hbase,依然使用sqooptest.Person表,導入前集群需啟動zookeeper和hbase。
sqoop命令
[hadoop@node01 ~]$ sqoop import \ > --connect jdbc:mysql://node01:3306/sqooptest \ > --username root \ > --password 123456 \ > --table Person \ > --hbase-table mysqltohbase \ # 指定hbase表名 > --hbase-create-table \ # hbase沒有表就創建表 > --column-family f1 \ # 指定列族 > --hbase-row-key id \ # 執行rowkey --m 1
執行完成后,hbase中查看發現新建了一張表,並且成功導入數據。
導出數據
sqoop導出數據,這里記錄從hdfs導出數據,如果是hive導出,也是直接讀取hdfs保存目錄中的文件進行導出,比較類似。
hdfs中先准備數據
[hadoop@node01 ~]$ hadoop fs -cat /hdfstomysql.txt 20/02/06 14:31:17 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable 1 messi 32 50 2 ronald 35 55 3 herry 40 51
mysql中需要先建表,否則會報錯。
CREATE TABLE sqooptest.hdfstomysql ( id INT NOT NULL, name varchar(100) NOT NULL, age INT NOT NULL, score INT NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
sqoop命令執行導出。
[hadoop@node01 ~]$ sqoop import \ > --connect jdbc:mysql://node01:3306/sqooptest \ > --username root \ > --password 123456 \ > --table hdfstomysql \ # 提前建立好的表 > --export-dir /hdfstomysql.txt \ # hdfs中目錄文件 > --input-fields-terminated-by " " # 指定文件數據的分隔符
導出后,發現mysql數據表中有了數據,ok。
以上,就是sqoop的使用入門,記錄一下以后使用。
參考博文:
(1)https://blogs.apache.org/sqoop/entry/apache_sqoop_highlights_of_sqoop#comment-1561314193000