Sqoop 初識

Sqoop是什麼?

Sqoop是一個用於在Hadoop和關係型數據庫之間高效傳輸海量數據的工具,它可以把數據從關係型數據庫中導入到HDFS中,也可以把HDFS中的數據導出到關係型數據庫中。

工作機制

它是怎麼在Hadoop和關係型數據庫之間傳輸數據的呢?

導入機制

先來看下它是怎麼把數據導入到HDFS中的,假設關係型數據庫爲MySQL。

首先,在Sqoop 導入開始之前,Sqoop需要和MySql數據庫建立連接(通常使用JDBC的方式建立連接),然後獲取要導入表的所有列和列的數據類型信息,並使用這些信息生成一個特定的Java類來存放從表中提取的記錄,且該類實現了DBWritable接口readFiles方法,該方法反序列化ResulrtSet來填充該類的字段值。在生成發序列化的代碼後,Sqoop會使用一個InputFormat來啓動MapReduce Job(InputFormat用來分區查詢表中的數據,默認爲DataDrivenDBInputFormat,它可以將分區查詢的任務分配給多個map task),然後Map Task執行查詢並從ResultSet中反序列化行(rows)到生成的類的實例上,在寫到HDFS之前,實例可以被直接寫入到SequenceFiles,也可以轉換以特定字符分割的文本。

Sqoop導入流程圖如下:


控制導入

有時,我們不需要導入整個表的數據,這是可以通過在查詢中指定WHERE子句來控制導入的範圍。

導入一致性

數據在導入HDFS中時,需要確保導入後的數據和源數據時一致的,因爲Map Task是運行一些並行且相互獨立的進程從數據庫中讀數據的,它們不能共享一個單獨的數據庫事務。所以,最好的方式是在導入數據時禁止更新數據。

增量導入

做增量導入,需要檢測出哪些是新增加的行,Sqoop提供了以下幾種方式來實現增量導入:

1、基於某一列的值,適用於自動增長序列的列,如ID。需要使用--incremental append 和--last-value 參數指定

2、基於行的修改時間:適用於被修改的記錄。需要--incremental lastmodified參數指定。

直接導入模式

直接導入就是通過不使用JDBC的方式來導入數據。例如MySQL的mysqldump。但是直接導入的方式不如JDBC的方式通用,因爲直接導入的方式不能處理像BLOB、CLOB類型的大對象數據。

導入大對象

大多數數據庫都提供了在單個字段裏存儲大量數據的能力,如存儲CLOB、BLOB類型的數據,這些大對象的數據在磁盤的存儲形式如下圖:


可以看到,關係型數據庫將大對象存儲在了主行外部的一塊獨立的存儲區域,而主行僅保留了對大對象數據的引用,只有在訪問大對象的時候纔打開它。

對於Hadoop MapReduce來說,庫表中的每條記錄在傳遞到mapper之前都要被物化,如果但條記錄比較大,將會降低物化效率,並且,在內存中完全物化大對象數據也是不可能的。

爲解決這些問題,Sqoop用一個單獨的文件來存儲大對象數據的記錄(record),稱作LobFile,在LobFile中的每個記錄都包含一個單獨的大對象,LobFile格式允許客戶端持有一個記錄的引用,而無需訪問記錄的內容。在訪問記錄時,將會使用java.io.InputStream或java.io.Reader來訪問。

當導入表中記錄時,“正常”的字段將會和一個LobFile(其存儲的是CLOB或BLOB的列)的引用一起被物化。導入一個含有大對象的記錄看起來就像下面的這樣:

2,gizmo,4.00,2009-11-30,4,null,externalLob(lf,lobfile0,100,5011714)
externalLob(...)文本是一個指向存儲在外部大對象的引用,以LobFile的格式(lf)存儲在一個名爲lobfile0的文件中,並且指定了其在文件中的偏移量(100)及長度(5011714)。

當讀取這條記錄的externalLob(...)時,將返回一個BlobRef類型的引用,它指向大對象列,但並不包含它的內容。BlobRef.getDataStream()方法才真正打開了LobFile文件並返回一個InputStream,這時你纔可以訪問大對象中的內容。這是一個很大的節省。

導出機制

再來看下Sqoop是怎樣把數據從HDFS導出到數據庫中的。

在執行導出之前,需要在庫中建立目標表,雖然Sqoop可以根據Java的類型推斷出適當的SQL數據類型,但這種轉化效果並不是很好,例如,Java的String類型,可以轉化爲Char(64)、VarChar(200)等。所以,你必須決定哪種類型是最適合的。

Sqoop的導出和導入很相似。在執行導出前,Sqoop需要和數據庫建立連接,通常使用JDBC,然後Sqoop根據目標表的定義生成一個Java類,該類具有從文本文件中解析記錄(records)和插入適當類型值到表中的能力(除了從ResultSet讀取列能力)。然後,啓動MapReduce Job,從HDFS讀取源數據文件(source datafiles),使用生成的Java類解析records,並執行所選擇的導出策略。

基於JDBC的導出策略建立在批處理的INSERT語句上,每次將添加多條記錄到目標表中。這將比單條插入要高效。

導出流程圖如下:


導出和事務

因爲Sqoop是使用多個task來並行導出數據切片(slices)的,這些並行導出並不是原子操作,它們會在不同的時間完成,意味着即使事務被用於task的內部,較早完成的任務的結果對另一個未完成任務可能是可見的。並且,數據庫通常是使用固定大小的緩衝區存儲事務,因此,一個事務並不一定能包含整個由一個task執行的所有操作。Sqoop是每幾千行提交一次結果,來確保不會耗盡內存。

爲解決這個問題,Sqoop會將數據導入到一個臨時表中,然後在Job結束時,如果導出成功,會使用一個事務將臨時表中的數據移動到目標表中。臨時表必須已經存在,並且要和目標表一樣的結構,並且還必須是空表,或者使用--clear-staging-table選項。

但是這樣做會很慢,因爲數據需要寫兩次:第一次寫到臨時表中,第二次寫到目標中。並且導出進程需要佔用更多的空間,因爲在臨時表中的數據移動到目標表中時,需要兩份的數據。

導出和SequenceFiles

Sqoop也可以導出存儲在SequenceFiles文件中的記錄到外部的庫表中。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章