1、sqoop使用
在學習sqoop使用之前,我們需要查看sqoop都是可以完成什麼任務,通過鍵入:sqoop help,我們就可以看到sqoop可以提供的服務。在項目中,我們主要使用的是sqoop import服務,在使用的過程中,我們還會經歷很多定製修改,講逐一講解。
1.1將數據從數據庫導入到hadoop中
導入指令:sqoop import –connect jdbc:mysql://hostname:port/database –username root –password 123456 –table example –m 1。在這裏講解一下指令的構成,如下:
1、--connect jdbc:mysql://hostname:port/database指定mysql數據庫主機名和端口號和數據庫名;
2、--username root 指定數據庫用戶名
3、--password 123456 指定數據庫密碼
4、--table example mysql中即將導出的表
5、-m 1 指定啓動一個map進程,如果表很大,可以啓動多個map進程
6、導入到HDFS中的路徑 默認:/user/grid/example/part-m-00000
注意:默認情況下,Sqoop會將我們導入的數據保存爲逗號分隔的文本文件。如果導入數據的字段內容存在分隔符,則我們可以另外指定分隔符、字段包圍字符和轉義字符。使用命令行參數可以指定分隔符、文件格式、壓縮以及對導入過程進行更細粒度的控制。
1.2、生成代碼
除了能夠將數據庫表的內容寫到HDFS,Sqoop還生成了一個Java源文件(example.java)保存在當前的本地目錄中。在運行了前面的sqoop import命令之後,可以通過ls example.java命令看到這個文件。代碼生成時Sqoop導入過程的必要組成部分,他是在Sqoop將源數據庫的表數據寫到HDFS之前,首先用生成的代碼對其進行反序列化。
生成的類中能夠保存一條從被導入表中取出的記錄。該類可以在MapReduce中使用這條記錄,也可以將這條記錄保存在HDFS中的一個SequenceFile文件中。在導入過程中,由Sqoop生成的SequenceFile文件會生成的類,將每一個被導入的行保存在其鍵值對格式中“值”的位置。
也許你不想將生成的類命名爲example,因爲每一個類的實例只對應與一條記錄。我們可以使用另外一個Sqoop工具來生成源代碼,並不執行導入操作,這個生成的代碼仍然會檢查數據庫表,以確定與每個字段相匹配的數據類型:
Sqoop codegen –connect jdbc:mysql://localhost/yidong –table example –class-name example
Codegen工具只是簡單的生成代碼,他不執行完整的導入操作。我們指定希望生成一個名爲example的類,這個類將被寫入到example.java文件中。在之前執行的導入過程中,我們還可以指定—class-name和其他代碼生成參數。如果你意外的刪除了生成的源代碼,或希望使用不同於導入過程的設定來生成代碼,都可以用這個工具來重新生成代碼。
如果計劃使用導入到SequenceFile文件中的記錄,你將不可避免的用到生成的類(對SequenceFile文件中的數據進行反序列化)。在使用文本文件中的記錄時,不需要用生成的代碼。
1.3、深入瞭解數據庫導入
在深入理解之前,我們需要先想一個問題:Sqoop是通過一個MapReduce作業從數據庫中導入一個表,這個作業從表中抽取一行行記錄,然後寫入到HDFS。MapReduce是如何記錄的?
下圖是Sqoop從數據庫中導入到HDFS的原理圖:
在導入開始之前,Sqoop使用JDBC來檢查將要導入的表。他檢索出表中所有的列以及列的SQL數據類型。這些SQL類型(VARCHAR、INTEGER)被映射到Java數據類型(String、Integer等),在MapReduce應用中將使用這些對應的java類型來保存字段的值。Sqoop的代碼生成器使用這些信息來創建對應表的類,用於保存從表中抽取的記錄。例如前面提到過的example類。
對於導入來說,更關鍵的是DBWritable接口的序列化方法,這些方法能使Widget類和JDBC進行交互:
Public void readFields(resultSet _dbResults)throws SQLException;
Public void write(PreparedStatement _dbstmt)throws SQLException;
JDBC的ResultSet接口提供了一個用戶從檢查結果中檢索記錄的遊標;這裏的readFields()方法將用ResultSet中一行數據的列來填充Example對象的字段。
Sqoop啓動的MapReduce作業用到一個InputFormat,他可以通過JDBC從一個數據庫表中讀取部分內容。Hadoop提供的DataDriverDBInputFormat能夠爲幾個Map任務對查詢結果進行劃分。爲了獲取更好的導入性能,查詢會根據一個“劃分列”來進行劃分的。Sqoop會選擇一個合適的列作爲劃分列(通常是表的主鍵)。
在生成反序列化代碼和配置InputFormat之後,Sqoop將作業發送到MapReduce集羣。Map任務將執行查詢並將ResultSet中的數據反序列化到生成類的實例,這些數據要麼直接保存在SequenceFile文件中,要麼在寫到HDFS之前被轉換成分割的文本。
Sqoop不需要每次都導入整張表,用戶也可以在查詢中加入到where子句,以此來限定需要導入的記錄:Sqoop –query <SQL>。
導入和一致性:在向HDFS導入數據時,重要的是要確保訪問的是數據源的一致性快照。從一個數據庫中並行讀取數據的MAP任務分別運行在不同的進程中。因此,他們不能共享一個數據庫任務。保證一致性的最好方法就是在導入時不允許運行任何進行對錶中現有數據進行更新。
1.4、使用導入的數據
一旦數據導入HDFS,就可以供定製的MapReduce程序使用。導入的文本格式數據可以供Hadoop Streaming中的腳本或者TextInputFormat爲默認格式運行的MapReduce作業使用。
爲了使用導入記錄的個別字段,必須對字段分割符進行解析,抽取出的字段值並轉換爲相應的數據類型。Sqoop生成的表類能自動完成這個過程,使你可以將精力集中在真正的要運行的MapReduce作業上。
1.5、導入的數據與hive
Hive和sqoop共同構成一個強大的服務於分析任務的工具鏈。Sqoop能夠根據一個關係數據源中的表來生成一個hive表。既然我們已經將表的數據導入到HDFS中,那麼就可以直接生成相應hive表的定義,然後加載保存在HDFS中的數據,例如:
Sqoop create-hive-table –connect jdbc:mysql://localhoust/yidong –table example –fields-terminated-by “,”
Load data inpath ‘example’ into table example
注:在爲一個特定的已導入數據集創建相應的hive表定義時,我們需要指定該數據集所使用的分隔符。否則,sqoop將允許hive使用自己默認的風格符。
如果想直接從數據庫將數據導入到hive,可以將上述三個步驟(將數據導入HDFS,創建hive表,將hdfs中的數據導入hive)縮短爲一個步驟。在進行導入時,sqoop可以生成hive表的定義,然後直接將數據導入hive表:
Sqoop import –connect jdbc:mysql://localhost/hadoopguide –table widgets –m 1 –hive-import
1.6、導入大對象
很多數據庫都具有在一個字段中保存大量數據的能力。取決於數據是文本還是二進制類型,通常這些類型爲CLOB或BLOB。數據庫一般會對這些“大對象”進行特殊處理。Sqoop將導入的大對象數據存儲在LobFile格式的單獨文件中,lobfile格式能夠存儲非常大的單條記錄。Lobfile文件中的每條記錄保存一個大對象。
在導入一條記錄時,所有的“正常”字段會在一個文本文件中一起物化,同時還生成一個指向保存CLOB或BLOB列的lobfile文件的引用。
2、執行導出
在sqoop中,導出是將hdfs作爲一個數據源,而將一個遠程的數據庫作爲目標。將一張表從hdfs導出到數據庫時,我們必須在數據庫中創建一張用於接收數據的目標表。雖然sqoop可以推斷出那個java類型適合存儲sql數據類型,但反過來確實行不通。因此,必須由用戶來確定哪些類型是最合適的。
例如:我們打算從hive中導出zip_profits表到mysql數據庫中。
①先在mysql中創建一個具有相同序列順序及合適sql表型的目標表:
Create table sales_by_sip(volume decimal(8,2),zip integer);
②接着運行導出命令:
Sqoop export –connect jdbc:mysql://localhost/hadoopguide –m 1 –table sales_by_zip –export-dir /user/hive/warehouse/zip_profits –input-fields-terminated-by “\0001”
③過mysql來確認導出成功:
mysql hadoopguide –e ‘select * from sales_by_zip’
注意:在hive中創建zip_profits表時,我們沒有指定任何分隔符。因此hive使用了自己的默認分隔符;但是直接從文件中讀取這張表時,我們需要將所使用的分隔符告知sqoop。Sqoop默認記錄是以換行符作爲分隔符。因此,可在sqoop export命令中使用—input-fields-terminated-by參數來指定字段分隔符。
2.1、深入瞭解導出
Sqoop導出功能的架構與其導入功能非常相似,在執行導出操作之前,sqoop會根據數據庫連接字符串來選擇一個導出方法。一般爲jdbc。然後,sqoop會根據目標表的定義生成一個java類。這個生成的類能夠從文本文件中解析記錄,並能夠向表中插入類型合適的值。接着會啓動一個MapReduce作業,從HDFS中讀取源數據文件,使用生成的類解析記錄,並且執行選定的導出方法。
基於jdbc的導出方法會產生一批insert語句,每條語句都會向目標表中插入多條記錄。多個單獨的線程被用於從HDFS讀取數據並與數據庫進行通信,以確保涉及不同系統的I/O操作能夠儘可能重疊執行。
雖然HDFS讀取數據的MapReduce作業大多根據所處理文件的數量和大小來選擇並行度(map任務的數量),但sqoop的導出工具允許用戶明確設定任務的數量。由於導出性能會受並行的數據庫寫入線程數量的影響,所以sqoop使用combinefileinput類將輸入文件分組分配給少數幾個map任務去執行。
2.2、導出與事務
進程的並行特性,導致導出操作往往不是原子操作。Sqoop會採用多個並行的任務導出,並且數據庫系統使用固定大小的緩衝區來存儲事務數據,這時一個任務中的所有操作不可能在一個事務中完成。因此,在導出操作進行過程中,提交過的中間結果都是可見的。在導出過程完成前,不要啓動那些使用導出結果的應用程序,否則這些應用會看到不完整的導出結果。
更有問題的是,如果任務失敗,他會從頭開始重新導入自己負責的那部分數據,因此可能會插入重複的記錄。當前sqoop還不能避免這種可能性。在啓動導出作業前,應當在數據庫中設置表的約束(例如,定義一個主鍵列)以保證數據行的唯一性。
2.3、導出與SequenceFile
Sqoop還可以將存儲在SequenceFile中的記錄導出到輸出表,不過有一些限制。SequenceFile中可以保存任意類型的記錄。Sqoop的導出工具從SequenceFile中讀取對象,然後直接發送到OutputCollector,由他將這些對象傳遞給數據庫導出OutputFormat。爲了能讓Sqoop使用,記錄必須被保存在SequenceFile鍵值對格式的值部分,並且必須繼承抽象類com.cloudera.sqoop.lib.SqoopRecord。
3、項目案例
在我們移動項目中,有些數據是通過web頁面維護,這些數據都是通過管理員手動添加到分析系統中的。爲了使hive可以更好的進行分析,所以需要將這些服務數據,定期導入到我們的hive數據倉儲中,這時sqoop就需要發揮作用了。
需要通過sqoop將數據從mysql導入到hive數據倉儲的服務有:應用管理、渠道管理、自定義事件管理、里程碑管理。他們對應的數據庫(10.6.219.86)表分別是:base_app、base_channel、base_event、base_milestone。
3.1、採用sqoop指令導入
在本次實踐中,我們僅以導入應用管理表(base_app)爲例進行說明。
1、執行sqoop指令將數據從mysql導入到hive中,指令爲:
Sqoop import --connect jdbc:mysql://10.1.11.78:3306/video --table base_event --username root --password 123456 -m 1 --hive-import --hive-database video --hive-table base_event --hive-overwrite --fields-terminated-by "\t"--lines-terminated-by “\n”--as-textfile
指令詳解:
- sqoop import ---執行sqoop導入指令;
- --connect jdbc:mysql://hostname:port/database ---要連接的數據庫地址、端口號、數據庫database;
- --table base_app ----- 要操作的數據庫表;
- --username root ----- 連接數據庫的用戶名;
- --password 123456 --- 連接數據庫的密碼;
- -m 1 ------ 要啓動的map數量
- --hive-import --- 採用hive方式導入
- [--create-hive-table] --- 如果導入的表在hive中不存在的話,sqoop自動在hive中創建該表。但是當表存在的情況下,添加該選項會導致指令報錯。所以在實際操作中,不建議使用,並且在實際的操作過程中,即使我們不添加該項輔助指令,sqoop也會在hive中創建導入的表。
- --hive-database yidong --- 要將數據庫表導入到hive的那個database中;
- --hive-table base_app --- 要將數據庫表導入到hive的那個表中;
- --hive-overwrite ---如果hive的表中已經存在數據,添加該項操作後,會將原有的數據覆蓋掉。
- --fields-terminated-by “\t” --- hive存儲到hdfs中的文件中字段間的分隔符;
- --lines-terminated-by “\n”– hive存儲到hdfs中的文件中每行間的分隔符;
- --as-textfile ---hive存儲到hdfs中的文件格式,採用文本存儲;
常用輔助指令詳解:
1、通過help指令查看sqoop導入幫助:
Sqoop help import;
2、Sqoop導入行輔助操作詳解:
3、Sqoop hive導入輔助操作詳解:
3.2、通過oozie編寫workflow定時將同步數據
在本次項目實踐中,我們需要用到oozie的sqoop action。在這章節中,我會演示給大家如何編寫workflow.xml實現定時數據同步的。
在項目運作的過程中發現,爲了能更好的進行數據同步,建議首先把需要同步的數據庫表在環境初始化的過程中,在hive數據倉儲中創建出來。這樣項目運作的過程中,會很輕鬆的執行。
編寫workflow.xml進行同步工作,workflow.xml內容如下:
在該例子中,我們是對四個需要同步的表進行,通過sqoop進行了同步。大家在編寫oozie sqoop action腳本的工程中,需要特別注意:sqoop支持兩種方式配置指令,一種是command,另一種是arg的方式。不管是採用哪種方式,導入導出的過程的命令中,一定不要出現sqoop,而是從sqoop需要執行的指令以後的內容開始(從import或者export開始),請查看截圖中的紅色標註。
3.3、項目實踐總結歸納
① 首先在初始化同步工作流環境的過程中,先在script.hql文件中編寫建表語句,在hive數據倉儲中創建出需要進行同步的table。
② 編寫workflow.xml文件,定義需要同步表的sqoop工作流,且工作流需要執行的命令中,不會出現“sqoop”關鍵字,而是從該關鍵字以後的指令開始,如:在cli情況下我們需要同步數據庫,執行的命令爲:sqoop import jdbc:mysql://……;而在workflow.xml配置的command命令中爲:import jdbc:mysql://…..,在這裏不用再填寫sqoop,否則會報錯,這是初學人員常遇到的錯誤,謹記!!!!
③ 在sqoop導入mysql數據到hive時,--fields-terminated-by "\t",自定義的分隔符要爲雙引號。否則指定的分隔符無效!
這些內容是從sqoop的官網整理出來的,是1.4.3版本的Document,如果有錯誤,希望大家指正。
1.使用sqoop導入數據
sqoop import --connect jdbc:mysql://localhost/db --username foo --table TEST
2.賬號密碼
sqoop import --connect jdbc:mysql://database.example.com/employees \ --username aaron --password 12345
3.驅動
sqoop import --driver com.microsoft.jdbc.sqlserver.SQLServerDriver --connect <connect-string> ...
4.寫sql語句導入的方式
sqoop import --query 'SELECT a.*, b.* FROM a JOIN b on (a.id == b.id) WHERE $CONDITIONS' --split-by a.id --target-dir /user/foo/joinresults
如果是順序導入的話,可以只開一個線程
sqoop import --query 'SELECT a.*, b.* FROM a JOIN b on (a.id == b.id) WHERE $CONDITIONS' -m 1 --target-dir /user/foo/joinresults
如果where語句中有要用單引號的,就像這樣子寫就可以啦"SELECT * FROM x WHERE a='foo' AND \$CONDITIONS"
5. 1.4.3版本的sqoop不支持複雜的sql語句,不支持or語句
6. --split-by <column-name>
默認是主鍵,假設有100行數據,它會執行那個SELECT * FROM sometable WHERE id >= lo AND id < hi,
with (lo, hi) 會分爲4次導入(0,250),(250,500),(500,750),(750,1001)
如果這個字段不能達到實際的劃分區域的效果,可以用別的字段。如果沒有索引列或者是組合主鍵的表,需要手動設置一個劃分列
7. --direct 是爲了利用某些數據庫本身提供的快速導入導出數據的工具,比如mysql的mysqldump
性能比jdbc更好,但是不知大對象的列,使用的時候,那些快速導入的工具的客戶端必須的shell腳本的目錄下
8.導入數據到hdfs目錄,這個命令會把數據寫到/shared/foo/ 目錄
sqoop import --connnect <connect-str> --table foo --warehouse-dir /shared \
或者
sqoop import --connnect <connect-str> --table foo --target-dir /dest \
9.傳遞參數給快速導入的工具,使用--開頭,下面這句命令傳遞給mysql默認的字符集是latin1
sqoop import --connect jdbc:mysql://server.foo.com/db --table bar \ --direct -- --default-character-set=latin1
10.轉換爲對象
--map-column-java <mapping> 轉換爲java數據類型
--map-column-hive <mapping> 轉轉爲hive數據類型
11.增加導入
--check-column (col) Specifies the column to be examined when determining which rows to import.
--incremental (mode) Specifies how Sqoop determines which rows are new. Legal values for mode include append and lastmodified.
--last-value (value) Specifies the maximum value of the check column from the previous import.
增加導入支持兩種模式append和lastmodified,用--incremental來指定
12.在導入大對象,比如BLOB和CLOB列時需要特殊處理,小於16MB的大對象可以和別的數據一起存儲,超過這個值就存儲在_lobs的子目錄當中
它們採用的是爲大對象做過優化的存儲格式,最大能存儲2^63字節的數據,我們可以用--inline-lob-limit參數來指定每個lob文件最大的限制是多少
如果設置爲0,則大對象使用外部存儲
13.分隔符、轉移字符
下面的這句話
Some string, with a comma.
Another "string with quotes"
使用這句命令導入$ sqoop import --fields-terminated-by , --escaped-by \\ --enclosed-by '\"' ...
會有下面這個結果
"Some string, with a comma.","1","2","3"...
"Another \"string with quotes\"","4","5","6"...
使用這句命令導入$ sqoop import --optionally-enclosed-by '\"' (the rest as above)...
"Some string, with a comma.",1,2,3...
"Another \"string with quotes\"",4,5,6...
14.hive導入參數
--hive-home <dir> 重寫$HIVE_HOME
--hive-import 插入數據到hive當中,使用hive的默認分隔符
--hive-overwrite 重寫插入
--create-hive-table 建表,如果表已經存在,該操作會報錯!
--hive-table <table-name> 設置到hive當中的表名
--hive-drop-import-delims 導入到hive時刪除 \n, \r, and \01
--hive-delims-replacement 導入到hive時用自定義的字符替換掉 \n, \r, and \01
--hive-partition-key hive分區的key
--hive-partition-value <v> hive分區的值
--map-column-hive <map> 類型匹配,sql類型對應到hive類型
15.hive空值處理
sqoop會自動把NULL轉換爲null處理,但是hive中默認是把\N來表示null,因爲預先處理不會生效的
我們需要使用 --null-string 和 --null-non-string來處理空值 把\N轉爲\\N
sqoop import ... --null-string '\\N' --null-non-string '\\N'
16.導入數據到hbase
導入的時候加上--hbase-table,它就會把內容導入到hbase當中,默認是用主鍵作爲split列
也可以用--hbase-row-key來指定,列族用--column-family來指定,它不支持--direct。
如果不想手動建表或者列族,就用--hbase-create-table參數
17.代碼生成參數,沒看懂
--bindir <dir> Output directory for compiled objects
--class-name <name> Sets the generated class name. This overrides --package-name. When combined with --jar-file, sets the input class.
--jar-file <file> Disable code generation; use specified jar
--outdir <dir> Output directory for generated code
--package-name <name> Put auto-generated classes in this package
--map-column-java <m> Override default mapping from SQL type to Java type for configured columns.
18.通過配置文件conf/sqoop-site.xml來配置常用參數
<property> <name>property.name</name> <value>property.value</value> </property>
如果不在這裏面配置的話,就需要像這樣寫命令
sqoop import -D property.name=property.value ...
19.兩個特別的參數
sqoop.bigdecimal.format.string 大decimal是否保存爲string,如果保存爲string就是 0.0000007,否則則爲1E7
sqoop.hbase.add.row.key 是否把作爲rowkey的列也加到行數據當中,默認是false的
20.例子
#指定列 $ sqoop import --connect jdbc:mysql://db.foo.com/corp --table EMPLOYEES \ --columns "employee_id,first_name,last_name,job_title" #使用8個線程 $ sqoop import --connect jdbc:mysql://db.foo.com/corp --table EMPLOYEES \ -m 8 #快速模式 $ sqoop import --connect jdbc:mysql://db.foo.com/corp --table EMPLOYEES \ --direct #使用sequencefile作爲存儲方式 $ sqoop import --connect jdbc:mysql://db.foo.com/corp --table EMPLOYEES \ --class-name com.foocorp.Employee --as-sequencefile #分隔符 $ sqoop import --connect jdbc:mysql://db.foo.com/corp --table EMPLOYEES \ --fields-terminated-by '\t' --lines-terminated-by '\n' --optionally-enclosed-by '\"' #導入到hive $ sqoop import --connect jdbc:mysql://db.foo.com/corp --table EMPLOYEES \ --hive-import #條件過濾 $ sqoop import --connect jdbc:mysql://db.foo.com/corp --table EMPLOYEES \ --where "start_date > '2010-01-01'" #用dept_id作爲分個字段 $ sqoop import --connect jdbc:mysql://db.foo.com/corp --table EMPLOYEES \ --split-by dept_id #追加導入 $ sqoop import --connect jdbc:mysql://db.foo.com/somedb --table sometable \ --where "id > 100000" --target-dir /incremental_dataset --append
21.導入所有的表sqoop-import-all-tables
每個表都要有主鍵,不能使用where條件過濾
sqoop import-all-tables --connect jdbc:mysql://db.foo.com/corp
22.export
我們採用sqoop-export插入數據的時候,如果數據已經存在了,插入會失敗
如果我們使用--update-key,它會認爲每個數據都是更新,比如我們使用下面這條語句
sqoop-export --table foo --update-key id --export-dir /path/to/data --connect … UPDATE foo SET msg='this is a test', bar=42 WHERE id=0; UPDATE foo SET msg='some more data', bar=100 WHERE id=1; ...
這樣即使找不到它也不會報錯
23.如果存在就更新,不存在就插入
加上這個參數就可以啦--update-mode allowinsert
24.事務的處理
它會一次statement插入100條數據,然後每100個statement提交一次,所以一次就會提交10000條數據
25.例子
$ sqoop export --connect jdbc:mysql://db.example.com/foo --table bar \ --export-dir /results/bar_data $ sqoop export --connect jdbc:mysql://db.example.com/foo --table bar \ --export-dir /results/bar_data --validate $ sqoop export --connect jdbc:mysql://db.example.com/foo --call barproc \ --export-dir /results/bar_data
sqoop中文參考手冊 :
https://wenku.baidu.com/view/7f3651ac58fb770bf68a5504.html
Sqoop用戶指南(v1.3.0-cdh3u6):
http://archive.cloudera.com/cdh/3/sqoop/SqoopUserGuide.html
Sqoop-1.4.6工具import和export使用詳解(官網)
(MySQL裏的數據)通過Sqoop Import Hive 裏 和 通過Sqoop Export Hive 裏的數據到(MySQL)
(MySQL裏的數據)通過Sqoop Import HDFS 裏 和 通過Sqoop Export HDFS 裏的數據到(MySQL)
使用sqoop從MySQL導入數據到HBase:
https://blog.csdn.net/Magic_Ninja/article/details/80555254
http://blog.5ibc.net/p/116730.html
https://www.2cto.com/net/201708/673854.html
使用sqoop從hive中導入數據到hbase:
https://blog.csdn.net/Magic_Ninja/article/details/80555254