1. 程式人生 > >Hive總結(十)Hive 輸入輸出適配類(輸出CSV,XML)

Hive總結(十)Hive 輸入輸出適配類(輸出CSV,XML)

在最初使用 hive ,應該說上手還是挺快的。 Hive 提供的類 SQL 語句與 mysql 語句極為相似,語法上有大量相同的地方,這給我們上手帶來了很大的方便,但是要得心應手地寫好這些語句,還需要對 hive 有較好的瞭解,才能結合 hive 特色寫出精妙的語句。

關於 hive 語言的詳細語法可參考官方 wiki 的語言手冊http://wiki.apache.org/hadoop/Hive/LanguageManual

雖然語法風格為我們提供了便利,但初次使用遇到的問題還是不少的,下面針對業務場景談談我們遇到的問題,和對 hive 功能的定製。

1、 分隔符問題

首先遇到的是日誌資料的分隔符問題,我們的日誌資料的大致格式如下:

2010-05-24 00:00:[email protected][email protected]@[email protected]@[email protected][email protected]

[email protected]@[email protected]@[email protected]@[email protected]@[email protected]@[email protected]@[email protected]

從格式可見其分隔符是“ @[email protected] ”,這是為了儘可能防止日誌正文出現與分隔符相同的字元而導致資料混淆。本來 hive支援在建表的時候指定自定義分隔符的,但經過多次測試發現只支援單個字元的自定義分隔符,像“ @[email protected] ”這樣的分隔符是不能被支援的,但是我們可以通過對分隔符的定製解決這個問題, hive 的內部分隔符是“ \001 ”,只要把分隔符替換成“\001 ”即可。

經過探索我們發現有兩條途徑解決這個問題。

a)自定義 outputformat 和 inputformat 。

Hive 的 outputformat/inputformat 與 hadoop 的 outputformat/inputformat 相當類似, inputformat 負責把輸入資料進行格式化,然後提供給 hive , outputformat 負責把 hive 輸出的資料重新格式化成目標格式再輸出到檔案,這種對格式進行定製的方式較為底層,對其進行定製也相對簡單,重寫 InputFormat 中 RecordReader 類中的 next 方法即可,示例程式碼如下:

  1. public boolean next(LongWritable key, BytesWritable value)
  2.         throws IOException {
  3.         while ( reader .next(key, text ) ) {
  4.         String strReplace = text .toString().toLowerCase().replace( "@[email protected]" , "\001" );
  5.         Text txtReplace = new Text();
  6.         txtReplace.set(strReplace );
  7.         value.set(txtReplace.getBytes(), 0, txtReplace.getLength());
  8.         return true ;
  9.       }
  10.          return false ;
  11. }
  12.         重寫 HiveIgnoreKeyTextOutputFormat 中 RecordWriter 中的 write 方法,示例程式碼如下:
  13.     public void write (Writable w) throws IOException {
  14.       String strReplace = ((Text)w).toString().replace( "\001" , "@[email protected]" );
  15.       Text txtReplace = new Text();
  16.       txtReplace.set(strReplace);
  17.       byte [] output = txtReplace.getBytes();
  18.       bytesWritable .set(output, 0, output. length );
  19.       writer .write( bytesWritable );
  20. }
複製程式碼

自定義 outputformat/inputformat 後,在建表時需要指定 outputformat/inputformat ,如下示例:

stored as INPUTFORMAT 'com.aspire.search.loganalysis.hive.SearchLogInputFormat' OUTPUTFORMAT 'com.aspire.search.loganalysis.hive.SearchLogOutputFormat'

b) 通過 SerDe(serialize/deserialize) ,在資料序列化和反序列化時格式化資料。

這種方式稍微複雜一點,對資料的控制能力也要弱一些,它使用正則表示式來匹配和處理資料,效能也會有所影響。但它的優點是可以自定義表屬性資訊 SERDEPROPERTIES ,在 SerDe 中通過這些屬性資訊可以有更多的定製行為。

2、 資料匯入匯出

a) 多版本日誌格式的相容

由於 hive 的應用場景主要是處理冷資料(只讀不寫),因此它只支援批量匯入和匯出資料,並不支援單條資料的寫入或更新,所以如果要匯入的資料存在某些不太規範的行,則需要我們定製一些擴充套件功能對其進行處理。

我們需要處理的日誌資料存在多個版本,各個版本每個欄位的資料內容存在一些差異,可能版本 A 日誌資料的第二個列是搜尋關鍵字,但版本 B 的第二列卻是搜尋的終端型別,如果這兩個版本的日誌直接匯入 hive 中,很明顯資料將會混亂,統計結果也不會正確。我們的任務是要使多個版本的日誌資料能在 hive 資料倉庫中共存,且表的 input/output 操作能夠最終對映到正確的日誌版本的正確欄位。

這裡我們不關心這部分繁瑣的工作,只關心技術實現的關鍵點,這個功能該在哪裡實現才能讓 hive 認得這些不同格式的資料呢?經過多方嘗試,在中間任何環節做這個版本適配都將導致複雜化,最終這個工作還是在 inputformat/outputformat 中完成最為優雅,畢竟 inputformat 是源頭, outputformat 是最終歸宿。具體來說,是在前面提到的 inputformat 的 next 方法中和在 outputformat 的 write 方法中完成這個適配工作。

b) Hive 操作本地資料

一開始,總是把本地資料先傳到 HDFS ,再由 hive 操作 hdfs 上的資料,然後再把資料從 HDFS 上傳回本地資料。後來發現大可不必如此, hive 語句都提供了“ local ”關鍵字,支援直接從本地匯入資料到 hive ,也能從 hive 直接匯出資料到本地,不過其內部計算時當然是用 HDFS 上的資料,只是自動為我們完成匯入匯出而已。

3、 資料處理

日誌資料的統計處理在這裡反倒沒有什麼特別之處,就是一些 SQL 語句而已,也沒有什麼高深的技巧,不過還是列舉一些語句示例,以示 hive 處理資料的方便之處,並展示 hive 的一些用法。

a) 為 hive 新增使用者定製功能,自定義功能都位於 hive_contrib.jar 包中

add jar /opt/hadoop/hive-0.5.0-bin/lib/hive_contrib.jar;

b)  統計每個關鍵詞的搜尋量,並按搜尋量降序排列,然後把結果存入表 keyword_20100603 中

create table keyword_20100603 as select keyword,count(keyword) as count from searchlog_20100603 group by keyword order by count desc;

c) 統計每類使用者終端的搜尋量,並按搜尋量降序排列,然後把結果存入表 device_20100603 中

create table device_20100603 as select device,count(device) as count from searchlog_20100603 group by device order by count desc;

d) 建立表 time_20100603 ,使用自定義的 INPUTFORMAT 和 OUTPUTFORMAT ,並指定表資料的真實存放位置在 '/LogAnalysis/results/time_20100603' ( HDFS 路徑),而不是放在 hive 自己的資料目錄中

create external table if not exists time_20100603(time string, count int) stored as INPUTFORMAT 'com.aspire.search.loganalysis.hive.XmlResultInputFormat' OUTPUTFORMAT 'com.aspire.search.loganalysis.hive.XmlResultOutputFormat' LOCATION '/LogAnalysis/results/time_20100603';

e) 統計每秒訪問量 TPS ,按訪問量降序排列,並把結果輸出到表 time_20100603 中,這個表我們在上面剛剛定義過,其真實位置在 '/LogAnalysis/results/time_20100603' ,並且由於 XmlResultOutputFormat 的格式化,檔案內容是 XML 格式。

insert overwrite table time_20100603 select time,count(time) as count from searchlog_20100603 group by time order by count desc;

f) 計算每個搜尋請求響應時間的最大值,最小值和平均值

insert overwrite table response_20100603 select max(responsetime) as max,min(responsetime) as min,avg(responsetime) as avg from searchlog_20100603;

g)建立一個表用於存放今天與昨天的關鍵詞搜尋量和增量及其增量比率,表資料位於 '/LogAnalysis/results/keyword_20100604_20100603' ,內容將是 XML 格式。

create external table if not exists keyword_20100604_20100603(keyword string, count int, increment int, incrementrate double) stored as INPUTFORMAT 'com.aspire.search.loganalysis.hive.XmlResultInputFormat' OUTPUTFORMAT 'com.aspire.search.loganalysis.hive.XmlResultOutputFormat' LOCATION '/LogAnalysis/results/keyword_20100604_20100603';

h)設定表的屬性,以便 XmlResultInputFormat 和 XmlResultOutputFormat 能根據 output.resulttype 的不同內容輸出不同格式的 XML 檔案。

alter table keyword_20100604_20100603 set tblproperties ('output.resulttype'='keyword');

i) 關聯今天關鍵詞統計結果表( keyword_20100604 )與昨天關鍵詞統計結果表( keyword_20100603 ),統計今天與昨天同時出現的關鍵詞的搜尋次數,今天相對昨天的增量和增量比率,並按增量比率降序排列,結果輸出到剛剛定義的 keyword_20100604_20100603 表中,其資料檔案內容將為 XML 格式。

insert overwrite table keyword_20100604_20100603 select cur.keyword, cur.count, cur.count-yes.count as increment, (cur.count-yes.count)/yes.count as incrementrate from keyword_20100604 cur join keyword_20100603 yes on (cur.keyword = yes.keyword) order by incrementrate desc;

4、使用者自定義函式 UDF

部分統計結果需要以 CSV 的格式輸出,對於這類檔案體全是有效內容的檔案,不需要像 XML 一樣包含 version , encoding 等資訊的檔案頭,最適合用 UDF(user define function) 了。

UDF 函式可直接應用於 select 語句,對查詢結構做格式化處理之後,再輸出內容。自定義 UDF 需要繼承 org.apache.hadoop.hive.ql.exec.UDF ,並實現 evaluate 函式, Evaluate 函式支援過載,還支援可變引數。我們實現了一個支援可變字串引數的 UDF ,支援把 select 得出的任意個數的不同型別資料轉換為字串後,按 CSV 格式輸出,由於程式碼較簡單,這裡給出原始碼示例:

  1. public String evaluate(String... strs) {
  2.        StringBuilder sb = new StringBuilder();
  3.        for ( int i = 0; i < strs. length ; i++) {
  4.            sb.append(ConvertCSVField(strs[i])).append( ',' );
  5.        }
  6.        sb.deleteCharAt(sb.length()-1);
  7.        return sb.toString();
  8. }
複製程式碼

需要注意的是,要使用 UDF 功能,除了實現自定義 UDF 外,還需要加入包含 UDF 的包,示例:

add jar /opt/hadoop/hive-0.5.0-bin/lib/hive_contrib.jar;

然後建立臨時方法,示例:

CREATE TEMPORARY FUNCTION Result2CSv AS ‘com.aspire.search.loganalysis.hive. Result2CSv';

使用完畢還要 drop 方法,示例:

DROP TEMPORARY FUNCTION Result2CSv;

5、輸出 XML 格式的統計結果

前面看到部分日誌統計結果輸出到一個表中,藉助 XmlResultInputFormat 和 XmlResultOutputFormat 格式化成 XML 檔案,考慮到建立這個表只是為了得到 XML 格式的輸出資料,我們只需實現 XmlResultOutputFormat 即可,如果還要支援 select 查詢,則我們還需要實現 XmlResultInputFormat ,這裡我們只介紹 XmlResultOutputFormat 。

前面介紹過,定製 XmlResultOutputFormat 我們只需重寫 write 即可,這個方法將會把 hive 的以 ’\001’ 分隔的多欄位資料格式化為我們需要的 XML 格式,被簡化的示例程式碼如下:

  1. public void write(Writable w) throws IOException {
  2.            String[] strFields = ((Text) w).toString().split( "\001" );
  3.            StringBuffer sbXml = new StringBuffer();
  4.            if ( strResultType .equals( "keyword" )) {
  5.     sbXml.append( "<record><keyword>" ).append(strFields[0]).append(
  6.     "</keyword><count>" ).append(strFields[1]).append(           "</count><increment>" ).append(strFields[2]).append(
  7.     "</increment><rate>" ).append(strFields[3]).append(
  8. "</rate></result>" );
  9.            }
  10.            Text txtXml = new Text();
  11.            byte [] strBytes = sbXml.toString().getBytes( "utf-8" );
  12.            txtXml.set(strBytes, 0, strBytes. length );
  13.            byte [] output = txtXml.getBytes();
  14.            bytesWritable .set(output, 0, output. length );
  15.            writer .write( bytesWritable );
  16.     }
複製程式碼

其中的 strResultType .equals( "keyword" ) 指定關鍵詞統計結果,這個屬性來自以下語句對結果型別的指定,通過這個屬性我們還可以用同一個 outputformat 輸出多種型別的結果。

alter table keyword_20100604_20100603 set tblproperties ('output.resulttype'='keyword');

仔細看看 write 函式的實現便可發現,其實這裡只輸出了 XML 檔案的正文,而 XML 的檔案頭和結束標籤在哪裡輸出呢?所幸我們採用的是基於 outputformat 的實現,我們可以在建構函式輸出 version , encoding 等檔案頭資訊,在 close() 方法中輸出結束標籤。

這也是我們為什麼不使用 UDF 來輸出結果的原因,自定義 UDF 函式不能輸出檔案頭和檔案尾,對於 XML 格式的資料無法輸出完整格式,只能輸出 CSV 這類所有行都是有效資料的檔案。

五、總結

Hive 是一個可擴充套件性極強的資料倉庫工具,藉助於 hadoop 分散式儲存計算平臺和 hive 對 SQL 語句的理解能力,我們所要做的大部分工作就是輸入和輸出資料的適配,恰恰這兩部分 IO 格式是千變萬化的,我們只需要定製我們自己的輸入輸出介面卡, hive將為我們透明化儲存和處理這些資料,大大簡化我們的工作。本文的重心也正在於此,這部分工作相信每一個做資料分析的朋友都會面對的,希望對您有益。

相關推薦

Hive總結Hive 輸入輸出輸出CSV,XML

在最初使用 hive ,應該說上手還是挺快的。 Hive 提供的類 SQL 語句與 mysql 語句極為相似,語法上有大量相同的地方,這給我們上手帶來了很大的方便,但是要得心應手地寫好這些語句,還

Android 自定義Dialog 有圓角和和已大螢幕的廣告機

在開發過程中,有時需要自定義的Dialog,設計的需求是,按照螢幕的比例來顯示Dialog,需要有圓角。效果如下圖。 自定義程式碼Dialog 程式碼如下: CustomDialog.class public class CustomDialog

總結:C語言字串輸入的三種方式scanf("%c",array); scanf("%s",str); gets(array);

1、以scanf("%c",&array);的格式輸入字串; 由於%c是輸入一個字元,若需要輸入字串時我們可以建立陣列或指標配合迴圈(while,do{}while,for)來達到目的。如下例利用do{}while及陣列來輸入字串,並重新輸出,以字元#為結束標誌: #includ

(轉載)人生中的20個故事 -幾年來,雞湯我只喝這一碗

喜歡 母牛 一次 中國歷史 依然 快樂 遇到 微軟 力量 1、斷箭 不相信自己的意誌,永遠也做不成將軍。 春秋戰國時代,一位父親和他的兒子出征打戰。父親已做了將軍,兒子還只是馬前卒。又一陣號角吹響,戰鼓雷鳴了,父親莊嚴地托起一個箭囊,其中插著一只箭。父親鄭重對兒子說:“

input標簽附帶提示文字bootstrap裏面輸入框的兩側同時添加額外元素

image for size 但是 元素符號 text tst form put 一直不太喜歡用定位,今天寫界面的時候,要在輸入框右邊添加默認的元素符號。 第一次嘗試,因為本身項目用的是bootstrap所以就想利用輸入框的兩側同時添加額外元素。 但是寫了代碼發現效果不

【轉】web app變革之rem手機屏幕實現全

理想 那種 內嵌 自己的 大屏幕 block 行業 尺寸 是我 以往web移動適配,常規寫法是:media only screen @media only screen and (min-device-width: 320px){ //針對iP

K均值聚K-means和高斯混合聚Mixture of Gaussian Models

math del 一個 ans line k-均值聚類 初始化 gaussian 樣本 K-means算法流程 給定條件: ????example set: \((x_1, y_1), (x_2, y_2), \dots, (x_N, y_N)\) 初始化: ????K個簇

給你一個全自動的屏幕方案基於SW方案!—— 解放你和UI的雙手

寬度 double 屏幕尺寸 高度 組件化 center ply mar 結束 Calces系列相關文章:Calces自動實現Android組件化模塊構建 前言 屏幕適配一直是移動端開發熱議的問題,但是適配方案往往在實際開發的時候會和UI提供的設計稿沖突。本文主要

異數OS TCP協議棧測試--網絡卡

異數OS TCP協議棧測試(四)–網絡卡適配篇 本文來自異數OS社群 github: https://github.com/yds086/HereticOS 異數OS社群QQ群: 652455784 異數OS-織夢師(訊息中介軟體)群: 476260389 為了實際走向應用,

小程式中使用rpx單位,佈局方案rpx、px、vw、vh

1:小程式中使用rpx單位 rpx單位是微信小程式中css的尺寸單位,rpx可以根據螢幕寬度進行自適應。 規定螢幕寬為750rpx。如在 iPhone6 上,螢幕寬度為375px,共有750個物理畫素,則750rpx = 375px = 750物理畫素,1rpx = 0.5px

android app版本升級DownloadManager、6.0、7.0

說明: 1.本文使用系統DownloadManager在通知欄更新下載進度  2.動態許可權使用第三方庫EasyPermissions(https://github.com/googlesamples/easypermissions)  3.下載完成的App安裝適配7.0 

關於Android 設定螢幕亮度Api23及更高版本

       這個需求應用場景主要是在有些頁面進入需要調節當前螢幕亮度,查詢相關資料都是api 23之前的,翻牆看了些文件,找到了解決方案,特此記錄下。     我們知道在api 23之後許可權需要動態去申請,但是有部分特殊許可權這樣是申請不到的,只有通過彈出設定窗口才能獲

html移動端頁面js採用rem+百分比形式

(function(win, lib) { var doc = win.document; var docEl = doc.documentElement; var metaEl = doc.querySelector('meta[name="vie

微信小程式填坑之路:佈局方案rpx、px、vw、vh

因為小程式是以微信為平臺執行的,可以同時執行在android與ios的裝置上,所以不可避免的會遇到佈局適配問題,特別是在iphone5上,因為螢幕尺寸小的緣故,也是適配問題最多的機型,下面就簡單介紹幾種適配方法。 rpx適配 rpx是小程式中

html5+css3問題手機、平板、PC

隨著網際網路的快速發展,以及html5+css3的迅速崛起。漸漸的響應式佈局,也會慢慢的出現在我們的視野裡,身為專業的web前端人員,還不學習新技術你就out啦!為什麼這樣說呢?因為響應式佈局能同時相容多個終端,比如(手機、平板、PC)做一個網站轉眼間就可以變成3個網站,和

理解html移動端開發螢幕問題包括小程式

二、理解淘寶、網易適配做法 網易的做法整理: 設計稿是基於iphone4或者iphone5來的,橫向解析度為640px,這時取一個100px的font-size為參照,那麼body元素的寬度就可以設定為width: 6.4rem,於是html的font-size=deviceWidth / 6.4。dev

svg 環形進度條加讀取進度效果讀數效果-- 移動端 rem

專案需求做一個環形進度條的效果需要有一個讀數的效果,在網上查閱相關資料後整理了下 gif 效果如下,錄製的不是很流暢多停留下時間看下 都是動態的效果 程式碼如下 <!DOCTYPE html> <html lang="en">

Util應用程式框架公共操作:資料型別轉換公共操作介紹篇

  本系列文章將介紹一些對初學者有幫助的輔助類,這些輔助類本身並沒有什麼稀奇之處,如何能發現需要封裝它們可能更加重要,所謂授之以魚不如授之以漁,掌握封裝公共操作類的技巧才是關鍵,我會詳細說明建立這些類的動機和思考過程,以幫助初學者發現和封裝自己需要的東西。建立公共操作類的技巧,大家可以參考我的這篇文章——應用

Android-X5WebView封裝Cookie管理、進度監聽、8.1系統等策略

本文已獨家授權 郭霖 ( guolin_blog ) 公眾號釋出! 擼完了上一篇Android-X5WebView簡介 之後,有些大兄弟可能覺得不過癮吶,說你那樣的都是很基礎的啊(的確很基礎),專案裡面用起來不爽啊(的確很不爽),不能讓我直接CV啊(的確不能直接複製貼上)等等,那這篇文

關於DPR2.0&DPR3.0手機上的問題focusdroid

DPR2.0&DPR3.0S手機適配問題 就一行程式碼解決問題,解決不了就去…… <template> <div> 這是商家+++頁面 <div class="box">