天天看點

跟我一起hadoop(3)——hive

hive架構圖:

跟我一起hadoop(3)——hive

使用者接口,包括 cli,client,wui。

中繼資料存儲,通常是存儲在關系資料庫如 mysql, derby 中。

解釋器、編譯器、優化器、執行器。

hadoop:用 hdfs 進行存儲,利用 mapreduce 進行計算。

使用者接口主要有三個:cli,client 和 wui。其中最常用的是 cli,cli 啟動的時候,會同時啟動一個 hive 副本。client 是 hive 的用戶端,使用者連接配接至 hive server。在啟動 client 模式的時候,需要指出 hive server 所在節點,并且在該節點啟動 hive server。 wui 是通過浏覽器通路 hive。

hive 将中繼資料存儲在資料庫中,如 mysql、derby。hive 中的中繼資料包括表的名字,表的列和分區及其屬性,表的屬性(是否為外部表等),表的資料所在目錄等。

解釋器、編譯器、優化器完成 hql 查詢語句從詞法分析、文法分析、編譯、優化以及查詢計劃的生成。生成的查詢計劃存儲在 hdfs 中,并在随後有 mapreduce 調用執行。

hive 的資料存儲在 hdfs 中,大部分的查詢由 mapreduce 完成(包含 * 的查詢,比如 select * from tbl 不會生成 mapredcue 任務)。

hive 中繼資料存儲

hive 将中繼資料存儲在 rdbms 中,有三種模式可以連接配接到資料庫:

single user mode: 此模式連接配接到一個 in-memory 的資料庫 derby,一般用于 unit test。

跟我一起hadoop(3)——hive

multi user mode:通過網絡連接配接到一個資料庫中,是最經常使用到的模式。

跟我一起hadoop(3)——hive

remote server mode:用于非 java 用戶端通路中繼資料庫,在伺服器端啟動一個 metastoreserver,用戶端利用 thrift 協定通過 metastoreserver 通路中繼資料庫。

跟我一起hadoop(3)——hive

hive 的資料存儲

首先,hive 沒有專門的資料存儲格式,也沒有為資料建立索引,使用者可以非常自由的組織 hive 中的表,隻需要在建立表的時候告訴 hive 資料中的列分隔符和行分隔符,hive 就可以解析資料。

其次,hive 中所有的資料都存儲在 hdfs 中,hive 中包含以下資料模型:table,external table,partition,bucket。

hive 中的 table 和資料庫中的 table 在概念上是類似的,每一個 table 在 hive 中都有一個相應的目錄存儲資料。例如,一個表 pvs,它在 hdfs 中的路徑為:/wh/pvs,其中,wh 是在 hive-site.xml 中由 ${hive.metastore.warehouse.dir} 指定的資料倉庫的目錄,所有的 table 資料(不包括 external table)都儲存在這個目錄中。

partition 對應于資料庫中的 partition 列的密集索引,但是 hive 中 partition 的組織方式和資料庫中的很不相同。在 hive 中,表中的一個 partition 對應于表下的一個目錄,所有的 partition 的資料都存儲在對應的目錄中。例如:pvs 表中包含 ds 和 city 兩個 partition,則對應于 ds = 20090801, ctry = us 的 hdfs 子目錄為:/wh/pvs/ds=20090801/ctry=us;對應于 ds = 20090801, ctry = ca 的 hdfs 子目錄為;/wh/pvs/ds=20090801/ctry=ca

buckets 對指定列計算 hash,根據 hash 值切分資料,目的是為了并行,每一個 bucket 對應一個檔案。将 user 列分散至 32 個 bucket,首先對 user 列的值計算 hash,對應 hash 值為 0 的 hdfs 目錄為:/wh/pvs/ds=20090801/ctry=us/part-00000;hash 值為 20 的 hdfs 目錄為:/wh/pvs/ds=20090801/ctry=us/part-00020

external table 指向已經在 hdfs 中存在的資料,可以建立 partition。它和 table 在中繼資料的組織上是相同的,而實際資料的存儲則有較大的差異。

table 的建立過程和資料加載過程(這兩個過程可以在同一個語句中完成),在加載資料的過程中,實際資料會被移動到資料倉庫目錄中;之後對資料對通路将會直接在資料倉庫目錄中完成。删除表時,表中的資料和中繼資料将會被同時删除。

external table 隻有一個過程,加載資料和建立表同時完成(create external table ……location),實際資料是存儲在 location 後面指定的 hdfs 路徑中,并不會移動到資料倉庫目錄中。

hive的執行原理

跟我一起hadoop(3)——hive

    hive建構在hadoop之上, 

    (1)hql中對查詢語句的解釋、優化、生成查詢計劃是由hive完成的 

    (2)所有的資料都是存儲在hadoop中 

    (3)查詢計劃被轉化為mapreduce任務,在hadoop中執行(有些查詢沒有mr任務,如:select * from table)

    (4)hadoop和hive都是用utf-8編碼的

    hive編譯器将一個hive ql轉換操作符。操作符operator是hive的最小的處理單元,每個操作符代表hdfs的一個操作或者一道mapreduce作業。operator都是hive定義的一個處理過程,其定義有:

protected list <operator<? extends serializable >> childoperators; 

protected list <operator<? extends serializable >> parentoperators; 

protected boolean done;   // 初始化值為false

    所有的操作構成了operator圖,hive正是基于這些圖關系來處理諸如limit, group by, join等操作。

跟我一起hadoop(3)——hive

hive ql的操作符

    操作符如下:

    tablescanoperator:掃描hive表資料

    reducesinkoperator:建立将發送到reducer端的<key,value>對

    joinoperator:join兩份資料

    selectoperator:選擇輸出列

    filesinkoperator:建立結果資料,輸出至檔案

    filteroperator:過濾輸入資料

    groupbyoperator:groupby語句

    mapjoinoperator:/*+mapjoin(t) */

    limitoperator:limit語句

    unionoperator:union語句

    hive通過execmapper和execreducer執行mapreduce任務。在執行mapreduce時有兩種模式,即本地模式和分布式模式 。

    hive編譯器的組成:

跟我一起hadoop(3)——hive
跟我一起hadoop(3)——hive

hive編譯器的組成

    編譯流程如下:

跟我一起hadoop(3)——hive
跟我一起hadoop(3)——hive

hive ql編譯流程

hive和資料庫的異同

    由于hive采用了sql的查詢語言hql,是以很容易将hive了解為資料庫。其實從結構上來看,hive和資料庫除了擁有類似的查詢語言,再無類似之處。資料庫可以用在online的應用中,但是hive是為資料倉庫而設計的,清楚這一點,有助于從應用角度了解hive的特性。

    hive和資料庫的比較如下表:

hive 

rdbms 

查詢語言 

hql

sql

資料存儲 

hdfs

raw device or local fs 

資料格式

使用者定義

系統決定

資料更新

不支援

支援

索引 

執行 

mapreduce 

executor

執行延遲 

處理資料規模 

可擴充性

     (1)查詢語言。由于 sql 被廣泛的應用在資料倉庫中,是以專門針對hive的特性設計了類sql的查詢語言hql。熟悉sql開發的開發者可以很友善的使用hive進行開發。

     (2)資料存儲位置。hive是建立在hadoop之上的,所有hive的資料都是存儲在hdfs中的。而資料庫則可以将資料儲存在塊裝置或者本地檔案系統中。

     (3)資料格式。hive中沒有定義專門的資料格式,資料格式可以由使用者指定,使用者定義資料格式需要指定三個屬性:列分隔符(通常為空格、”\t”、”\x001″)、行分隔符(”\n”)以及讀取檔案資料的方法(hive中預設有三個檔案格式textfile,sequencefile以及rcfile)。由于在加載資料的過程中,不需要從使用者資料格式到hive定義的資料格式的轉換,是以,hive在加載的過程中不會對資料本身進行任何修改,而隻是将資料内容複制或者移動到相應的hdfs目錄中。而在資料庫中,不同的資料庫有不同的存儲引擎,定義了自己的資料格式。所有資料都會按照一定的組織存儲,是以,資料庫加載資料的過程會比較耗時。

     (4)資料更新。由于hive是針對資料倉庫應用設計的,而資料倉庫的内容是讀多寫少的。是以,hive中不支援對資料的改寫和添加,所有的資料都是在加載的時候中确定好的。而資料庫中的資料通常是需要經常進行修改的,是以可以使用insert into … values添加資料,使用update … set修改資料。

     (5)索引。之前已經說過,hive在加載資料的過程中不會對資料進行任何處理,甚至不會對資料進行掃描,是以也沒有對資料中的某些key建立索引。hive要通路資料中滿足條件的特定值時,需要暴力掃描整個資料,是以通路延遲較高。由于mapreduce的引入, hive可以并行通路資料,是以即使沒有索引,對于大資料量的通路,hive仍然可以展現出優勢。資料庫中,通常會針對一個或者幾個列建立索引,是以對于少量的特定條件的資料的通路,資料庫可以有很高的效率,較低的延遲。由于資料的通路延遲較高,決定了hive不适合線上資料查詢。

     (6)執行。hive中大多數查詢的執行是通過hadoop提供的mapreduce來實作的(類似select * from tbl的查詢不需要mapreduce)。而資料庫通常有自己的執行引擎。

     (7)執行延遲。之前提到,hive在查詢資料的時候,由于沒有索引,需要掃描整個表,是以延遲較高。另外一個導緻hive執行延遲高的因素是mapreduce架構。由于mapreduce本身具有較高的延遲,是以在利用mapreduce執行hive查詢時,也會有較高的延遲。相對的,資料庫的執行延遲較低。當然,這個低是有條件的,即資料規模較小,當資料規模大到超過資料庫的處理能力的時候,hive的并行計算顯然能展現出優勢。

     (8)可擴充性。由于hive是建立在hadoop之上的,是以hive的可擴充性是和hadoop的可擴充性是一緻的(世界上最大的hadoop叢集在yahoo!,2009年的規模在4000台節點左右)。而資料庫由于acid語義的嚴格限制,擴充行非常有限。目前最先進的并行資料庫oracle在理論上的擴充能力也隻有100台左右。

     (9)資料規模。由于hive建立在叢集上并可以利用mapreduce進行并行計算,是以可以支援很大規模的資料;對應的,資料庫可以支援的資料規模較小。

hive基本操作

    create table語句的一些注意項:

    (1)create table建立一個指定名字的表。如果相同名字的表已經存在,則抛出異常;使用者可以用if not exist選項來忽略這個異常。

   (2)external 關鍵字可以讓使用者建立一個外部表,在建表的同時指定一個指向實際資料的路徑( location ),hive 建立内部表時,會将資料移動到資料倉庫指向的路徑;若建立外部表,僅記錄資料所在的路徑,不對資料的位置做任何改變。在删除表的時候,内部表的中繼資料和資料會被一起删除,而外部表隻删除中繼資料,不删除資料。

    (3)like允許使用者複制現有的表結構,但是不複制資料。

    (4)使用者在建表的時候可以自定義serde或者使用自帶的 serde ( serialize/deserilize 的簡稱,目的是用于序列化和反序列化 )。如果沒有指定 row format 或者 row format delimited,将會使用自帶的serde。在建表的時候,使用者還需要為表指定列,使用者在指定表的列的同時也會指定自定義的serde,hive通過serde确定表的具體的列的資料。

    (5)如果檔案資料是純文字,可以使用stored as textfile。如果資料需要壓縮,使用stored as sequence。

    (6)有分區的表可以在建立的時候使用 partitioned b y語句。一個表可以擁有一個或者多個分區,每一個分區單獨存在一個目錄下。而且,表和分區都可以對某個列進行clustered by操作,将若幹個列放入一個桶(bucket)中。也可以利用sort by對資料進行排序。這樣可以為特定應用提高性能。

    (7)表名和列名不區分大小寫,serde和屬性名區分大小寫。表和列的注釋是字元串。

    alter table語句:主要功能包括add partitions, drop partitions, rename table, change column, add/replace columns。

    create view語句:建立視圖。格式create view [if not exists] view_name [ (column_name [comment column_comment], …) ]

    showy語句:show tables;  show partitions; describe檢視表結構。

    load語句:hive裝載資料時沒有做任何轉換,加載到表中的資料隻是進入相應的配置單元表的位置。load操作隻是單純的複制/移動操作,将資料檔案移動到hive表對應的位置。

    insert語句:插入資料。hive不支援一條一條的用 insert 語句進行插入操作,這個應該是與hive的storage layer是有關系的,因為它的存儲層是hdfs,插入一個資料要全表掃描,還不如用整個表的替換來的快些。hive也不支援update的操作。資料是以load的方式,加載到建立好的表中。資料一旦導入,則不可修改。要麼drop掉整個表,要麼建立新的表,導入新的資料。

    drop語句:删除一個内部表的同時會同時删除表的中繼資料和資料。删除一個外部表,隻删除中繼資料而保留資料。

    limit子句:可以限制查詢的記錄數。查詢的結果是随機選擇的。下面的查詢語句從 t1 表中随機查詢5條記錄,select * from t1 limit 5。

    top k查詢:下面的查詢語句查詢銷售記錄最大的 5 個銷售代表。

set mapred.reduce.tasks = 1

select * from sales sort by amount desc limit 5

    正規表達式使用:select語句可以使用正規表達式做列選擇,下面的語句查詢除了ds和h 之外的所有列:

select `(ds|hr)?+.+` from sales

    select語句:查詢資料。

    group by, order by, sort by子句:聚合可進一步分為多個表,甚至發送到 hadoop 的 dfs 的檔案(可以進行操作,然後使用hdfs的utilitites)。可以用hive.map.aggr控制怎麼進行彙總。預設為為true,配置單元會做的第一級聚合直接在map上的任務。這通常提供更好的效率,但可能需要更多的記憶體來運作成功。

    join語句:連接配接操作。一些注意事項:

    (1)hive隻支援等值連接配接(equality joins)、外連接配接(outer joins)和(left/right joins)。hive不支援所有非等值的連接配接,因為非等值連接配接非常難轉化到map/reduce任務。

    (2)hive 支援多于2個表的連接配接。

    (3)join 時,每次 map/reduce 任務的邏輯: reducer 會緩存 join 序列中除了最後一個表的所有表的記錄, 再通過最後一個表将結果序列化到檔案系統。這一實作有助于在reduce端減少記憶體的使用量。實踐中,應該把最大的那個表寫在最後(否則會因為緩存浪費大量記憶體)。

    (4)left,right 和 full outer 關鍵字用于處理 join 中空記錄的情況。

    (5)left semi join 是 in/exists 子查詢的一種更高效的實作。hive 目前沒有實作 in/exists 子查詢,是以你可以用 left semi join 重寫你的子查詢語句。left semi join的限制是, join子句中右邊的表隻能在on子句中設定過濾條件,在where子句、select子句或其他地方過濾都不行。

使用hive注意點

    (1)字元集

    hadoop和hive都是用utf-8編碼的,是以, 所有中文必須是utf-8編碼, 才能正常使用。

    備注:中文資料load到表裡面,,如果字元集不同,很有可能全是亂碼需要做轉碼的,但是hive本身沒有函數來做這個。

    (2)壓縮

    hive.exec.compress.output 這個參數,預設是false,但是很多時候貌似要單獨顯式設定一遍,否則會對結果做壓縮的,如果你的這個檔案後面還要在hadoop下直接操作,那麼就不能壓縮了。

    (3)count(distinct)

    目前的hive不支援在一條查詢語句中有多distinct。如果要在hive查詢語句中實作多distinct,需要使用至少n+1條查詢語句(n為distinct的數目),前n條查詢分别對n個列去重,最後一條查詢語句對n個去重之後的列做join操作,得到最終結果。

    (4)join

    隻支援等值連接配接

    (5)dml操作

    隻支援insert/load操作,無update和delte

    (6)having

    不支援having操作。如果需要這個功能要嵌套一個子查詢用where限制

    (7)子查詢

    hive不支援where子句中的子查詢

    (8)join中處理null值的語義差別

    sql标準中,任何對null的操作(數值比較,字元串操作等)結果都為null。hive對null值處理的邏輯和标準基本一緻,除了join時的特殊邏輯。這裡的特殊邏輯指的是,hive的join中,作為join key的字段比較,null=null是有意義的,且傳回值為true。

    (9)分号字元

    分号是sql語句結束标記,在hiveql中也是,但是在hiveql中,對分号的識别沒有那麼智慧,例如:

select concat(cookie_id,concat(‘;’,’zoo’)) from c02_clickstat_fatdt1 limit 2;

failed: parse error: line 0:-1 cannot recognize input ‘<eof>’ in function specification

    可以推斷,hive解析語句的時候,隻要遇到分号就認為語句結束,而無論是否用引号包含起來。

    解決的辦法是,使用分号的八進制的ascii碼進行轉義,那麼上述語句應寫成: 

select concat(cookie_id,concat(‘\073′,’zoo’)) from c02_clickstat_fatdt1 limit 2;

    為什麼是八進制ascii碼?我嘗試用十六進制的ascii碼,但hive會将其視為字元串處理并未轉義,好像僅支援八進制,原因不詳。這個規則也适用于其他非select語句,如create table中需要定義分隔符,那麼對不可見字元做分隔符就需要用八進制的ascii碼來轉義。

    (10)insert

    根據文法insert必須加“overwrite”關鍵字,也就是說每一次插入都是一次重寫。