標籤:方式 服務 假設 star 解壓 apach 一個 理想 寫入
本節作為《Hadoop從入門到精通》大型專題的第三章第二節將教大家如何在Mapreduce中使用XML和JSON兩大常見格式,並分析比較最適合Mapreduce大資料處理的資料格式。
在本章的第一章節介紹中,我們簡單瞭解了Mapreduce資料序列化的概念,以及其對於XML和JSON格式並不友好。本節作為《Hadoop從入門到精通》大型專題的第三章第二節將教大家如何在Mapreduce中使用XML和JSON兩大常見格式,並分析比較最適合Mapreduce大資料處理的資料格式。
MapReuce中對大資料處理最合適的資料格式是什嗎?
3.2.1 XML
XML自1998年誕生以來就作為一種資料格式來表示機器和人類都可讀的資料。它成為系統之間資料交換的通用語言,現在被許多標準所採用,例如SOAP和RSS,並且被用作Microsoft Office等產品的開放資料格式。
MapReduce和XML
MapReduce捆綁了與文本一起使用的InputFormat,但沒有支援XML,也就是說,原生Mapreduce對XML十分不友好。在MapReduce中平行處理單個XML檔案很棘手,因為XML不包含其資料格式的同步標記。
問題
希望在MapReduce中使用大型XML檔案,並能夠並行拆分和處理。
解決方案
Mahout的XMLInputFormat可用於MapReduce處理HDFS中的XML檔案。 它讀取由特定XML開始和結束標記分隔的記錄,此技術還解釋了如何在MapReduce中將XML作為輸出發送。
MapReduce不包含對XML的內建支援,因此我們轉向另一個Apache項目——Mahout,一個提供XML InputFormat的機器學習系統。 要瞭解XML InputFormat,你可以編寫一個MapReduce作業,該作業使用Mahout的XML輸入格式從Hadoop的設定檔(HDFS)中讀取屬性名稱和值。
第一步是對作業進行配置:
Mahout的XML輸入格式很簡陋,我們需要指定檔案搜尋的確切開始和結束XML標記,並使用以下方法拆分檔案(並提取記錄):
檔案沿著HDFS塊邊界分成不連續的部分,用於資料本地化。
每個map任務都在特定的輸入拆分上運行,map任務尋求輸入拆分的開始,然後繼續處理檔案,直到第一個xmlinput.start。
重複發出xmlinput.start和xmlinput.end之間的內容,直到輸入拆分的末尾。
接下來,你需要編寫一個映射器來使用Mahout的XML輸入格式。Text表單已提供XML元素,因此需要使用XML解析器從XML中提取內容。
表3.1 使用Java的STAX解析器提取內容
該map具有一個Text執行個體,該執行個體包含start和end標記之間資料的String表示。在此代碼中,我們可以使用Java的內建Streaming API for XML(StAX)解析器提取每個屬性的鍵和值並輸出。
如果針對Cloudera的core-site.xml運行MapReduce作業並使用HDFS cat命令顯示輸出,將看到以下內容:
此輸出顯示已成功使用XML作為MapReduce的輸入序列化格式。不僅如此,還可以支援巨大的XML檔案,因為輸入格式支援拆分XML。
寫XML
當可以正常讀XML之後,我們要解決的就是如何寫XML。 在reducer中,調用main reduce方法之前和之後都會發生回調,可以使用它來發出開始和結束標記,如下所示。
表3.2 用於發出開始和結束標記的reducer
這也可以嵌入到OutputFormat中。
Pig
如果想在Pig中使用XML,Piggy Bank library(使用者貢獻的Pig程式碼程式庫)包含一個XMLLoader。其工作方式與此技術非常相似,可捕獲開始和結束標記之間的所有內容,並將其作為Pig元組中的單位元組數組欄位提供。
Hive
目前沒有辦法在Hive中使用XML,必須寫一個自訂SerDe。
總結
Mahout的XmlInputFormat可協助使用XML,但它對開始和結束元素名稱的精確字串匹配很敏感。如果元素標記包含具有變數值的屬性,無法控制元素產生或者可能導致使用XML命名空間限定符,則此方法不可用。
如果可以控制輸入中的XML,則可以通過在每行使用單個XML元素來簡化此練習。這允許使用內建的MapReduce基於文本的輸入格式(例如TextInputFormat),它將每一行視為記錄並拆分。
值得考慮的另一個選擇是預先處理步驟,可以將原始XML轉換為每個XML元素的單獨行,或者將其轉換為完全不同的資料格式,例如SequenceFile或Avro,這兩種格式都解決了拆分問題。
現在,你已經瞭解如何使用XML,讓我們來處理另一種流行的序列化格式JSON。
3.2.2 JSON
JSON共用XML的機器和人類可讀特徵,並且自21世紀初以來就存在。它比XML簡潔,但是沒有XML中豐富的類型和驗證功能。
如果有一些代碼正在從流式REST服務中下載JSON資料,並且每小時都會將檔案寫入HDFS。由於下載的資料量很大,因此產生的每個檔案大小為數千MB。
如果你被要求編寫一個MapReduce作業,需要將大型JSON檔案作為輸入。你可以將問題分為兩部分:首先,MapReduce沒有與JSON一起使用的InputFormat; 其次,如何分割JSON?
圖3.7顯示了拆分JSON問題。 想象一下,MapReduce建立了一個拆分,。對此輸入拆分進行操作的map任務將執行對輸入拆分的搜尋,以確定下一條記錄的開始。對於諸如JSON和XML之類的檔案格式,由於缺少同步標記或任何其他標識記錄開頭,因此知道下一條記錄何時開始是很有挑戰性的。
JSON比XML等格式更難分割成不同的段,因為JSON沒有token(如XML中的結束標記)來表示記錄的開頭或結尾。
問題
希望在MapReduce中使用JSON輸入,並確保可以為並發讀取分區輸入JSON檔案。
解決方案
Elephant Bird LzoJsonInputFormat被用來作為建立輸入格式類以使用JSON元素的基礎,該方法可以使用多行JSON。
圖3.7 使用JSON和多個輸入拆分的問題樣本
討論
Elephant Bird(https://github.com/kevinweil/elephant-bird)是一個開源項目,包含用於處理LZOP壓縮的有用程式,它有一個可讀取JSON的LzoJsonInputFormat,儘管要求輸入檔案是LZOP-compressed。,但可以將Elephant Bird代碼用作自己的JSON InputFormat模板,該模板不具有LZOP compression要求。
此解決方案假定每個JSON記錄位於單獨的行上。JsonRecordFormat很簡單,除了構造和返回JsonRecordFormat之外什麼也沒做,所以我們將跳過該代碼。JsonRecordFormat向映射器發出LongWritable,MapWritable key/value,其中MapWritable是JSON元素名稱及其值的映射。
我們來看看RecordReader的工作原理,它使用LineRecordReader,這是一個內建的MapReduce讀取器。要將該行轉換為MapWritable,讀取器使用json-simple解析器將該行解析為JSON對象,然後迭代JSON對象中的鍵並將它們與其關聯值一起放到MapWritable。映射器在LongWritable中被賦予JSON資料,MapWritable pairs可以相應地處理資料。
以下顯示了JSON對象樣本:
該技巧假設每行一個JSON對象,以下代碼顯示了在此樣本中使用的JSON檔案:
現在將JSON檔案複製到HDFS並運行MapReduce代碼。MapReduce代碼寫入每個JSON key/value對並輸出:
寫JSON
類似於3.2.1節,編寫XML的方法也可用於編寫JSON。
Pig
Elephant Bird包含一個JsonLoader和LzoJsonLoader,可以使用它來處理Pig中的JSON,這些載入器使用基於行的JSON。每個Pig元組都包含該行中每個JSON元素的chararray欄位。
Hive
Hive包含一個可以序列化JSON的DelimitedJSONSerDe類,但遺憾的是無法對其進行還原序列化,因此無法使用此SerDe將資料載入到Hive中。
總結
此解決方案假定JSON輸入的結構為每個JSON對象一行。那麼,如何使用跨多行的JSON對象?GitHub上有一個項目( https://github.com/alexholmes/json-mapreduce)可以在單個JSON檔案上進行多個輸入拆分,此方法可搜尋特定的JSON成員並檢索包含的對象。
你可以查看名為hive-json-serde的Google項目,該項目可以同時支援序列化和還原序列化。
正如你所看到的,在MapReduce中使用XML和JSON是非常糟糕的,並且對如何布局資料有嚴格要求。MapReduce對這兩種格式的支援也很複雜且容易出錯,因為它們不適合拆分。顯然,需要查看具有內部支援且可拆分的替代檔案格式。
下一步是研究更適合MapReduce的複雜檔案格式,例如Avro和SequenceFile。
3.3 大資料序列化格式
當使用scalar或tabular資料時,非結構化文字格式設定很有效。諸如XML和JSON之類的半結構化文字格式設定可以對包括複合欄位或分層資料的複雜資料結構進行建模。但是,當處理較大資料量時,我們更需要具有緊湊序列化表單的序列化格式,這些格式本身支援分區並具有模式演變功能。
在本節中,我們將比較最適合MapReduce大資料處理的序列化格式,並跟進如何將它們與MapReduce一起使用。
3.3.1 比較SequenceFile,Protocol Buffers,Thrift和Avro
根據經驗,在選擇資料序列化格式時,以下特徵非常重要:
代碼產生——某些序列化格式具有代碼產生作用的庫,允許產生豐富的對象,使更容易與資料互動。產生的程式碼還提供了類似安全性等額外好處,以確保消費者和生產者使用正確的資料類型。
架構演變 - 資料模型隨著時間的推移而發展,重要的是資料格式支援修改資料模型的需求。模式演變功能允許你添加、修改並在某些情況下刪除屬性,同時為讀和寫提供向後和向前相容性。
語言支援 - 可能需要使用多種程式設計語言訪問資料,主流語言支援資料格式非常重要。
資料壓縮 - 資料壓縮非常重要,因為可以使用大量資料。並且,理想的資料格式能夠在寫入和讀取時內部壓縮和解壓縮資料。如果資料格式不支援壓縮,那麼對於程式員而言,這是一個很大的問題,因為這意味著必須將壓縮和解壓縮作為資料管道的一部分進行管理(就像使用基於文本的檔案格式一樣)。
可拆分性 - 較新的資料格式支援多個並行讀取器,可讀取和處理大型檔案的不同塊。檔案格式包含同步標記至關重要(可隨機搜尋和掃描到下一條記錄開頭)。
支援MapReduce和Hadoop生態系統 - 選擇的資料格式必須支援MapReduce和其他Hadoop生態系統關鍵項目,例如Hive。如果沒有這種支援,你將負責編寫代碼以使檔案格式適用於這些系統。
表3.1比較了流行的資料序列化架構,以瞭解它們如何相互疊加。以下討論提供了有關這些技術的其他背景知識。
表3.1資料序列化架構的功能比較
讓我們更詳細地看一下這些格式。
SequenceFile
建立SequenceFile格式是為了與MapReduce、Pig和Hive一起使用,因此可以很好地與所有工具整合。缺點主要是缺乏代碼產生和版本控制支援,以及有限的語言支援。
Protocol Buffers
Protocol Buffers 已被Google大量用於互操作,其優勢在於其版本支援二進位格式。缺點是MapReduce(或任何第三方軟體)缺乏對讀取Protocol Buffers 序列化產生的檔案支援。但是,Elephant Bird可以在容器檔案中使用Protocol Buffers序列化。
Thrift
Thrift是Facebook內部開發的資料序列化和RPC架構,在本機資料序列化格式中不支援MapReduce,但可以支援不同的wire-level資料表示,包括JSON和各種二進位編碼。 Thrift還包括具有各種類型伺服器的RPC層。本章將忽略RPC功能,並專註於資料序列化。
Avro
Avro格式是Doug Cutting建立的,旨在協助彌補SequenceFile的不足。
Parquet
Parquet是一種具有豐富Hadoop系統支援的柱狀檔案格式,可以與Avro、Protocol Buffers和Thrift等友好工作。儘管 Parquet 是一個面向列的檔案格式,不要期望每列一個資料檔案。Parquet 在同一個資料檔案中儲存一行中的所有資料,以確保在同一個節點上處理時一行的所有列都可用。Parquet 所做的是設定 HDFS 塊大小和最大資料檔案大小為 1GB,以確保 I/O 和網路傳輸請求適用於大批量資料。
基於上述評估標準,Avro似乎最適合作為Hadoop中的資料序列化架構。SequenceFile緊隨其後,因為它與Hadoop具有內在相容性(它設計用於Hadoop)。
你可以在Github上查看jvm-serializers項目,該項目運行各種基準測試,以根據序列化和還原序列化時間等比較檔案格式。它包含Avro,Protocol Buffers和Thrift基準測試以及許多其他架構。
在瞭解了各種資料序列化架構後,我們將在接下來幾節中專門討論這些格式。
MapReuce中對大資料處理最合適的資料格式是什嗎?