sqoop關係型數據遷移原理以及map端內存爲什麼不會爆掉窺探
序:map客戶端使用jdbc向數據庫發送查詢語句,將會拿到全部數據到map的客戶端,安裝jdbc的原理,數據所有緩存在內存中,可是內存沒有出現爆掉狀況,這是由於1.3之後,對jdbc進行了優化,改進jdbc內部原理,將數據寫入磁盤存儲了。html
原文和做者一塊兒討論: http://www.cnblogs.com/intsmaze/p/6775034.html前端
微信:intsmazejava
避免微信回覆重複諮詢問題,技術諮詢請博客留言。python
Sqoop是apache旗下一款「Hadoop和關係數據庫服務器之間傳送數據」的工具。Sqoop架構很是簡單,其整合了Hive、Hbase和Oozie,經過map-reduce任務來傳輸數據,從而提供併發特性和容錯。mysql
導入數據:MySQL,Oracle導入數據到Hadoop的HDFS、HIVE、HBASE等數據存儲系統。git
導出數據:從Hadoop的文件系統中導出數據到關係數據庫mysql等。程序員
工做機制
將導入或導出命令翻譯成mapreduce程序來實現,在翻譯出的mapreduce中主要是對inputformat和outputformat進行定製。web
Sqoop的數據導入
從RDBMS導入單個表到HDFS。表中的每一行被視爲HDFS的記錄。全部記錄都存儲爲文本文件的文本數據(或者Avro、sequence文件等二進制數據)
redis
表數據:在mysql中有一個庫test中intsmaze表。spring
導入intsmaze表數據到HDFS
bin/sqoop import \ --connect jdbc:mysql://192.168.19.131:3306/test \ --username root \ --password hadoop \ --table intsmaze \ --m 1
若是成功執行,那麼會獲得下面的輸出。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
17/04/25 03:15:06 INFO mapreduce.Job: Running job: job_1490356790522_0018
17/04/25 03:15:52 INFO mapreduce.Job: Job job_1490356790522_0018 running in uber mode : false
17/04/25 03:15:52 INFO mapreduce.Job: map 0% reduce 0%
17/04/25 03:16:13 INFO mapreduce.Job: map 100% reduce 0%
17/04/25 03:16:14 INFO mapreduce.Job: Job job_1490356790522_0018 completed successfully
17/04/25 03:16:15 INFO mapreduce.Job: Counters: 30
File System Counters......
Job Counters ......
Map-Reduce Framework......
File Input Format Counters
Bytes Read=0
File Output Format Counters
Bytes Written=22
17/04/25 03:16:15 INFO mapreduce.ImportJobBase: Transferred 22 bytes in 98.332 seconds (0.2237 bytes/sec)
17/04/25 03:16:15 INFO mapreduce.ImportJobBase: Retrieved 3 records.
|
原理解析:
查看HDFS導入的數據,intsmaze表的數據和字段之間用逗號(,)表示。
1
2
3
|
1,2,22
2,3,33
3,ad,12
|
默認狀況下,Sqoop會將咱們導入的數據保存爲逗號分隔的文本文件。若是導入數據的字段內容存在逗號分隔符,咱們能夠另外指定分隔符,字段包圍字符和轉義字符。使用命令行參數能夠指定分隔符,文件格式,壓縮等。支持文本文件(--as-textfile)、avro(--as-avrodatafile)、SequenceFiles(--as-sequencefile)。默認爲文本。
Sqoop啓動的mapreduce做業會用到一個InputFormat,它能夠經過JDBC從一個數據庫表中讀取部份內容。Hadoop提供的DataDrivenDBInputFormat可以爲幾個map任務對查詢結果進行劃分。
使用一個簡單的查詢一般就能夠讀取一張表的內容
1
|
select col1,col2,... form tablename
|
可是爲了更好的導入性能,能夠將查詢劃分到多個節點上執行。查詢時根據一個劃分列(肯定根據哪個列劃分)來進行劃分。根據表中的元數據,Sqoop會選擇一個合適的列做爲劃分列(一般是表的主鍵)。主鍵列中的最小值和最大值會被讀出,與目標任務數一塊兒來肯定每一個map任務要執行的查詢。固然用戶也可使用split-by參數本身指定一個列做爲劃分列。
例如:person表中有10000條記錄,其id列值爲0~9999。在導入這張表時,Sqoop會判斷出id是表的主鍵列。啓動MapReduce做業時,用來執行導入的DataDrivenDBInputFormat便會發出一條相似於select min(id),max(id) form intsmaze的查詢語句。假設咱們制定運行5個map任務(使用-m 5),這樣即可以確認每一個map任務要執行的查詢分別爲select id,name,... form intsmaze where id>=0 and id<2000,select id,name,... form intsmaze where id>=2000 and id<4000,...,依次類推。
注意:劃分列的選擇是影響並行執行效率的重要因素。若是id列的值不是均勻分佈的(好比id值從2000到4000的範圍是沒有記錄的),那麼有一部分map任務可能只有不多或沒有工做要作,而其餘任務則有不少工做要作。
嚴重注意:在1.3以前,map的並行度必定要設置好,由於map客戶端會向數據庫發送查詢語句,將會拿到全部數據到map的客戶端緩存到,而後在執行map()方法一條一條處理,全部若是設置很差,一個map拿到的表數據過大就會內存溢出,畢竟裏面是用jdbc去獲取的,全部數據都裝在jdbc的對象中,爆是必然的。在1.3之後改寫jdbc的內部原理,拿到一條數據就寫入硬盤中,就沒有內存溢出了。
增量導入
Sqoop不須要每次都導入整張表。例如,能夠指定僅導入表的部分列。用戶也能夠在查詢中加入where子句,來限定須要導入的記錄。例如,若是上個月已經將id爲0~9999的記錄導入,而本月新增了1000條記錄,那麼在導入時的查詢語句中加入子句where id>=10000,來實現只導入全部新增的記錄。
它須要添加incremental,check-column,和last-value選項來執行增量導入。
下面的語法用於Sqoop導入命令增量選項。
--incremental <mode> --check-column <column name> --last value <last check column value>
假設新添加的數據轉換成intsmaze表以下:
下面的命令用於在intsmaze表執行增量導入。
1
2
3
4
5
6
|
bin/sqoop import --connect jdbc:mysql://192.168.19.131:3306/test --username root --password hadoop \
--table person \
--m 1 \
--incremental append \
--check-column id \
--last-value 3
|
執行增量導入時,則會在hdfs上默認路徑下新增一個文件來存儲導入的新增數據,如上面的part-m-00001。
part-m-00001文件的數據內容爲:
4,aa,4 5,bb,5 6,cc,6
導入到HDFS指定目錄
在使用Sqoop導入表數據到HDFS,咱們能夠指定目標目錄。
--target-dir <new or exist directory in HDFS>
下面的命令是用來導入emp_add表數據到'/queryresult'目錄。
bin/sqoop import \ --connect jdbc:mysql://192.168.19.131:3306/test \ --username root \ --password hadoop \ --target-dir /queryresult \ --table intsmaze \ --m 1
實際場景的分析:我一開始擔憂在導入增量數據時,數據文件的位置等問題,想過經過每次執行增量導入時來根據時間做爲文件名來指定每一次導入時文件存儲在hdfs上的路徑來解決。如今看來不須要擔憂這個問題了。可是考慮這樣一種狀況:關係庫中的某張表天天增量導入到hdfs上,而後使用hive對導入的數據加載進hive表時,咱們不該該每次都狀況hive表再進行全局導入hive,這樣太耗費效率了。固然能夠根據文件的生成時間來肯定每次把那個文件導入到hive中,可是不便於維護,能夠直接根據目錄名來導入該目錄下的數據到hive中,且導入到hive中的數據能夠按天設置分區,每次導入的數據進入一個新的分區。
有些業務場景只須要對hive表中天天新增的那些數據進行etl便可,徹底沒有必要每次都是將整個hive表進行清理,那麼能夠結合hive的分區,按天進行分區,這樣每次進行etl處理就處理那一個分區數據便可。固然有些數據好比兩表的join操做,則必須對全表進行處理,那麼在join時不限制分區便可,數據倒入時仍然時間分區裝載數據。
導入關係表到HIVE
bin/sqoop import --connect jdbc:mysql://192.168.19.131:3306/test --username root --password root --table intsmaze --hive-import --m 1
sqoop import --connect jdbc:mysql://192.168.19.131:3306/hive --username root --password admin --table intsmaze --fields-terminated-by '\t' --null-string '**' -m 1 --append --hive-import --check-column 'TBL_ID' --incremental append --last-value 6
導入表數據子集
Sqoop導入"where"子句的一個子集。它執行在各自的數據庫服務器相應的SQL查詢,並將結果存儲在HDFS的目標目錄。
where子句的語法以下。
--where <condition>
導入intsmaze表數據的子集。子集查詢檢全部列可是居住城市爲:sec-bad
bin/sqoop import \ --connect jdbc:mysql://192.168.19.131:3306/test \ --username root \ --password root \ --where "city ='sec-bad'" \ --target-dir /wherequery \ --table intsmaze --m 1
按需導入
bin/sqoop import \ --connect jdbc:mysql://192.168.19.131:3306/test \ --username root \ --password root \ --target-dir /wherequery2 \ --query 'select id,name,deg from intsmaze WHERE id>1207 and $CONDITIONS' \ --split-by id \ --fields-terminated-by '\t' \ --m 1
$CONDITIONS參數是固定的,必需要寫上。
支持將關係數據庫中的數據導入到Hive(--hive-import)、HBase(--hbase-table)
數據導入Hive分三步:1)導入數據到HDFS 2)Hive建表 3)使用「LOAD DATA INPAHT」將數據LOAD到表中
數據導入HBase分二部:1)導入數據到HDFS 2)調用HBase put操做逐行將數據寫入表
導入表數據因爲字段存在空字符串或null致使的問題
bin/sqoop import --connect jdbc:mysql://192.168.19.131:3306/test --username root --password hadoop \ --table intsmaze \ --m 1 \ --incremental append \ --check-column id \ --last-value 6
咱們查看hdfs上的數據
7,null,7 8,null,8
MySQL(或者別的RDBMS)導入數據到hdfs後會發現原來在mysql中字段值明明是NULL, 到Hive查詢後 where field is null 會沒有結果呢,而後經過檢查一看,NULL值都變成了字段串'null'。其實你在導入的時候加上如下兩個參數就能夠解決了,
--null-string '\\N' --null-non-string '\\N'
這裏要注意一點。在hive裏面。NULL是用\N來表示的。你能夠本身作個實驗 insert overwrite table tb select NULL from tb1 limit 1;而後在去查看原文件就能夠發現了。多提一點,若是在導入後發現數據錯位了,或者有好多原來有值的字段都變成了NULL, 這是由於你原表varchar類型的字段中可能含有\n\r等一些特殊字符。能夠加上 --hive-drop-import-delims