5. Hadoop之旅——Hive使用篇

github鏈接

針對Hive的優化主要有以下幾個方面:

  1. map
  2. reduce
  3. file format
  4. shuffle & sort
  5. job as whole
  6. job chain
Hive job 分解

Hive map reduce 的過程如下:

Hive map-reduce

Map 階段優化

Map 階段的優化,主要是控制 map size的大小, map任務的數量
Map task num = Total Input size / map size
map size = max{ ${mapred.min.split.size}, // 數據的最小分割單元大小, 可調整,默認1B min( ${dfs.block.size}, // hdfs 上數據塊大小, 有hdfs配置決定 ${mapred.max.split.size}) // 數據最大分隔單元大小, 可調整,默認256MB }
直接調整 mapred.map.tasks 這個參數是沒有效果
根據map階段的使用時間,來調整數據輸入大小

  • Map 階段, 小文件對map任務數影響很大, 可以用以下參數合并小文件

Map輸入合并小文件對應參數:

      - set mapred.max.split.size=256000000;     // 每個Map最大輸入大小
      - set mapred.min.split.size.per.node=100000000;   // 一個節點上map最小分割,決定結點間是否合并
      - set mapred.min.split.size.per.rack=100000000;  // 一個機架下map最小分割,決定機架間是否合并
      - set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;   
        // 執行Map前進行小文件合并, 合并的大小由`mapred.max.split.size`參數決定

Map與Reduce之間的優化(spill, copy, sort phase)

  1. map phase和reduce phase之間主要有3道工序
    • 首先要把map輸出的結果進行排序后做成中間文件
    • 其次這個中間文件就能分發到各個reduce
    • 最后reduce端在執行reduce phase之前把收集到的排序子文件合并成一個排序文件

    需要強調的是,雖然這個部分可以調的參數挺多,但是大部分在一般情況下都是不要調整的,除非能精準的定位到這個部分有問題。

Spill 與 Sort

 由于內存不夠,局部排序的內容會寫入磁盤文件,這個過程叫做spill.
  spill出來的文件再進行merge
1. io.sort.mb 控制mapper buffer大小,影響spill的發生時機。 buffer滿的時候,觸發spill
2. io.sort.factor 控制merge階段合并的文件大小, 默認10(個文件)
  > 調整參數,由spill的時間和merge時間決定。io.sort.mb不能超過map的jvm heap size。
    Reduce端的merge也是一樣可以用io.sort.factor。
    一般情況下這兩個參數很少需要調整,除非很明確知道這個地方是瓶頸。比如如果map端的輸出太大,    要么是每個map讀入了很大的文件(比如不能split的大gz壓縮文件),要么是計算邏輯導致輸出膨脹了很多倍。

Copy

也就是shuffle,這個階段把文件從map端copy到reduce端。
mapred.reduce.slowstart.completed.maps map完成多少的時候,啟動copy。默認5%
tasktracker.http.threads 作為server端的map用于提供數據傳輸服務的線程
mapred.reduce.parallel.copies 作為client端的reduce同時從map端拉取數據的并行度(一次同時從多少個map拉數據)
mapred.job.shuffle.input.buffer.percent 控制shuffle buffer占reduce task heap size的大小,默認0.7(70%)

tasktracker.http.threadsmapred.reduce.parallel.copies 需要相互配合。
shuffle階段可能會出現的OOM。一般認為是內存分配不合理,GC無法及時釋放內存導致。
可以嘗試調低mapred.job.shuffle.input.buffer.percent 或者增加 reduce 數量 mapred.reduce.tasks

  1. Map輸出合并
  - set hive.merge.mapfiles = true  // 在Map-only的任務結束時合并小文件
  - set hive.merge.mapredfiles = true  // 在Map-Reduce的任務結束時合并小文件
  - set hive.merge.size.per.task = 256*1000*1000 // 合并文件的大小
  - set hive.merge.smallfiles.avgsize=16000000
     // 當輸出文件的平均大小小于該值時,啟動一個獨立的map-reduce任務進行文件merge

Reduce 階段優化

reduce phase 優化

  1. 可以直接控制reduce的數量, mapred.reduce.tasks 參數
  2. 靈活配置,Hive自動計算reduce數量, 公式:
    num_reduce_tasks = min { ${hive.exec.reducers.max}, // 默認999, 上限 ${input.size} / ${ hive.exec.reducers.bytes.per.reducer} }
    hive.exec.reducers.bytes.per.reducer // 默認1G

文件格式的優化

Hive0.9版本有3種,textfile,sequencefile和rcfile。總體上來說,rcfile的壓縮比例和查詢時間稍好。

關于使用方法,可以在建表結構時可以指定格式,然后指定壓縮插入:
create table rc_file_test( col int ) stored as rcfile;
set hive.exec.compress.output = true;
insert overwrite table rc_file_test select * from source_table;
也可以通過hive.default.fileformat
來設定輸出格式,適用于create table as select的情況:
set hive.default.fileformat = SequenceFile;
set hive.exec.compress.output = true; 
/*對于sequencefile,有record和block兩種壓縮方式可選,block壓縮比更高*/
set mapred.output.compression.type = BLOCK; 
create table seq_file_testas select * from source_table;

上面的文件格式轉換,其實是由hive完成的(也就是插入動作)。但是也可以由外部直接導入純文本啟用壓縮(lzo壓縮),或者是由MapReduce Job生成的數據。

  1. Lzo壓縮

lzo 文件支持split, 適合hdfs存儲。 啟用lzo壓縮特別對于小規模集群還是很有用的,壓縮比率大概能達到原始日志大小的1/4左右。
Hadoop原生是支持gzip和bzip2壓縮, 壓縮比率比lzo大,但是不支持split,所以速度很慢。
不過lzo不比gzip和bzip2,不是linux系統原生支持的,需要安裝至少lzo,lzop 以及 hadoop lzo jar.
同時,配置hadoop里面 core-site.xml, mapred-site.xml

core-site.xml

<property> 
        <name>io.compression.codecs</name>
        <value>org.apache.hadoop.io.compress.DefaultCodec,com.hadoop.compression.lzo.LzoCodec,com.hadoop.compression.lzo.LzopCodec,org.apache.hadoop.io.compress.GzipCodec,org.apache.hadoop.io.compress.BZip2Codec</value> 
</property> 
<property> 
        <name>io.compression.codec.lzo.class</name> 
        <value>com.hadoop.compression.lzo.LzoCodec</value> 
</property> 

使用lzo,lzop,gzip,bzip2壓縮作為io壓縮的編解碼器,并指定lzo的類
mapred-site.xml

<property> 
        <name>mapred.compress.map.output</name> 
        <value>true</value> 
</property> 
<property> 
        <name>mapred.map.output.compression.codec</name> 
        <value>com.hadoop.compression.lzo.LzoCodec</value> 
</property> 
<property> 
        <name>mapred.child.java.opts</name> 
        <value>-Djava.library.path=/opt/hadoopgpl/native/Linux-amd64-64</value> 
</property> 

map結果采用壓縮輸出,可以降低網絡帶寬的使用,并指定map輸出所使用的lzo的類。以及指定編解碼器所在位置。


創建lzo索引:

hadoop jar /opt/hadoopgpl/lib/hadoop-lzo.jar    \
com.hadoop.compression.lzo.LzoIndexer       \
/path/to/lzo/file/or/path

在streaming中使用lzo:

hadoop jar /usr/share/hadoop/contrib/streaming/hadoop-streaming-1.0.3.jar \ 
-file map.py \ 
-file red.py \ 
-mapper map.py \ 
-reducer red.py \ 
-inputformat com.hadoop.mapred.DeprecatedLzoTextInputFormat \ 
-input /data/rawlog/test/20130325 -output /tmp/test_20130325

以及在hive中指定壓縮編解碼器:
hadoop集群啟用了壓縮,就需要在Hive建表的時候指定壓縮時所使用的編解碼器,否則Hive無法正確讀取數據。
Gzip和Bzip2由于是hadoop默認支持的,所以無需指定特殊的編解碼器,只要指定Text類型即可。

CREATE EXTERNAL TABLE text_test_table( 
      id string,
      name string) 
      ROW FORMAT DELIMITED  
      FIELDS TERMINATED BY '\t'  
      STORED AS TEXTFILE  
      LOCATION 
            'hdfs://hadoopmaster:9000/data/dw/adpv/20130325' 

而LZO是外掛的第三方庫,所以要指定輸入和輸出的編解碼器。

CREATE EXTERNAL TABLE lzo_test_table( 
      id string,
      name string) 
      ROW FORMAT DELIMITED  
      FIELDS TERMINATED BY '\t'  
      STORED AS INPUTFORMAT  
      'com.hadoop.mapred.DeprecatedLzoTextInputFormat'  
      OUTPUTFORMAT  
      'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat' 
      LOCATION 
            'hdfs://hadoopmaster:9000/data/dw/adpv/20130325' 

對于日志文件,可以本地lzop壓縮好,然后推到hdfs。
另外,對hadoop的jobconf做一個指定。這樣就可以做到,輸入是lzo,輸出也可以lzo。或者輸入是text,輸出是lzo。

-inputformat com.hadoop.mapred.DeprecatedLzoTextInputFormat  \
-jobconf mapred.output.compress=true  \
-jobconf mapred.output.compression.codec=com.hadoop.compression.lzo.LzopCodec 

最后對HDFS上的日志做Indexer(創建lzo索引),這樣Hive或者MR,可以把大的lzo壓縮文件,切分多個map計算(splittable)

注意
hive讀取sequencefile,會忽略key,(比如,lzo文件就是key-value),直接讀value,并且按照指定分隔符分隔value,獲得字段值。
但是如果hive的數據來源是從mr生成的,那么寫sequencefile的時候,key和value都是有意義的,key不能被忽略,而是應該當成第一個字段。為了解決這種不匹配的情況,有兩種辦法:
- 一是要求凡是結果會給hive用的mr job輸出value的時候帶上key。但是這樣的話對于開發是一個負擔,讀寫數據的時候都要注意這個情況。
- 二是把這個交給hive解決,寫一個InputFormat包裝一下,把value輸出加上key即可。以下是核心代碼,修改了RecordReader的next方法:

//注意:這里為了簡化,假定了key和value都是Text類型,所以MR的輸出的k/v都要是Text類型。
//這個簡化還會造成數據為空時,出現org.apache.hadoop.io.BytesWritable cannot be cast to org.apache.hadoop.io.Text的錯誤,因為默認hive的sequencefile的key是一個空的ByteWritable。
public synchronized boolean next(K key, V value) throws IOException { 
          Text tKey = (Text) key; 
          Text tValue = (Text) value; 
          if (!super.next(innerKey, innerValue)) 
               return false;
          Text inner_key = (Text) innerKey; //在構造函數中用createKey()生成 
          Text inner_value = (Text) innerValue; //在構造函數中用createValue()生成
          tKey.set(inner_key);
          tValue.set(inner_key.toString() + '\t' + inner_value.toString()); // 分隔符注意自己定義
          return true;
}
  1. Map 輸入輸出其他壓縮 ( hadoop 2.x)
    除了lzo, 常用的還有snappy壓縮等。
    比起zlib,snappy壓縮大多數情況下都會更快,但是壓縮后大小會大20%到100%。
    snappy跟lzo同屬于Lempel–Ziv 壓縮算法家族,但是優于lzo的兩點是:
    1)Snappy解壓縮比 LZO 更快, 壓縮速度相當, meaning thetotal round-trip time is superior.
  1. Snappy 是 BSD-licensed, 可以集成在 Hadoop。 LZO 是 GPL-licensed, 需要單獨安裝。
    通常集群會使用lzo做map reduce的中間結果壓縮,中間結果是用戶不可見的,一般是mapper寫入磁盤,并供reducer讀取。中間結果對壓縮友好,一是key有冗余,而是需要寫入磁盤,壓縮減少IO量。同時,lzo和snappy都不是CPU密集的壓縮算法,不會造成map,reduce的CPU時間缺乏。 而且snappy的效率比lzo要高20%。
    壓縮算法比較

    需要注意的一點是,Snappy旨在與容器格式(例如序列文件或Avro數據文件)一起使用,而不是直接在純文本上使用,因為后者不可拆分(not splittable),無法處理并行使用MapReduce。 這不同于LZO,其中可以索引LZO壓縮文件以確定分裂點,使得LZO文件可以在后續處理中被有效地處理。
    如圖,.snappy與.lzo都不是splittable的,lzo可以通過創建index文件彌補,snappy適合用在序列文件( Sequence Files)上,比如常用的在map階段的輸出,如下:
    使用
    core-site.xml:
    io.compression.codecs 增加 org.apache.hadoop.io.compress.SnappyCodec
    mapred-site.xml:
<property>
        <name>mapred.compress.map.output</name>
        <value>true</value>
 </property>
<property>
        <name>mapred.map.output.compression.codec</name>
        <value>org.apache.hadoop.io.compress.SnappyCodec</value>
</property>

Job整體優化

從job的整體上,看是否有可以優化的點,以下分別陳述幾個:

Job執行模式

hadoop map-reduce 三種模式,local、偽分布式、分布式。
分布式模式,需要啟動分布式job,調度資源,啟動時間比較長。對于很小的job可以采用local map-reduce 模式。參數如下:

`hive.exec.mode.local.auto`=true // 就可以自動開啟local模式,同時需要以下兩個參數配合
`hive.exec.mode.local.auto.tasks.max`  // 默認4
`hive.exec.mode.local.auto.inputbytes.max`  // 默認 128(M)
默認map task數少于4(參考文章上面提到的map任務的決定因素),并且總輸入大小不超過128M,則自動開啟local模式。
另外,簡單的select語句,比如select limit 10, 這種取少量sample的方式,那么在hive0.10之后有專門的fetch task優化,使用參數hive.fetch.task.conversion。Hadoop 2.x 應該是默認開啟的。

JVM重用

正常情況下,MapReduce啟動的JVM在完成一個task之后就退出了,但是如果任務花費時間很短,又要多次啟動JVM的情況下(比如對很大數據量進行計數操作),JVM的啟動時間就會變成一個比較大的overhead。在這種情況下,可以使用jvm重用的參數:

  set mapred.job.reuse.jvm.num.tasks=5;  // 一個jvm完成多少個task之后再退出

適合小任務較多的場景,對于大任務影響不大,而且考慮大任務gc的時間,啟動新的jvm成本可以忽略。

索引

Hive可以針對列建立索引,在以下情況需要使用索引:

  1. 數據集很大
  2. 查詢時間大大超過預期
  3. 需要快速查詢
  4. 當構建數據模型的時候
    Hive的索引管理在獨立的表中(類似于lookup table,而不是傳統數據庫的B-tree),不影響數據表。另一個優勢是索引同樣可以被partition,決定于數據的大小。
  • 索引類型
    • Compact Indexing

    Compact indexing stores the pair of indexed column’s value and its blockid.

    • Bitmap Indexing (在hive0.8出行,通常以用于列值比較獨特的場景(distinct values) )

    Bitmap indexing stores the combination of indexed column value and list of rows as a bitmap.

  • 創建索引

CREATE INDEX index_name
 
ON TABLE table_name (columns,....)
 
AS 'org.apache.hadoop.hive.ql.index.compact.CompactIndexHandler'    // create a compact index
 
WITH DEFERRED REBUILD;   // This statement means we need to alter the index in later stages

這個語句會創建一個索引,但是要完成創建,還需要完成rebuild語句。通過添加一個或多個更改語句,會開啟一個map-reudce 任務來完成索引創建。

ALTER INDEX index_nam on table_name REBUILD;  //  This ALTER statement will complete our REBUILDED index creation for the table.
  • 查看表的索引
show formatted index on table_name;
  • 創建bitmap索引
CREATE INDEX index_name
 
ON TABLE table_name (age)
 
AS 'BITMAP'
 
WITH DEFERRED REBUILD;
 
ALTER INDEX index_name on table_name REBUILD;
  • 刪除索引
DROP INDEX IF EXISTS olympic_index ON olympic;

Note:

  1. 同一個表,創建不同類型的index在相同的列上,先創建的index會被使用
  2. 索引會加快query執行速度
  3. 一個表能創建任意數量的索引
  4. 使用什么索引,依賴于數據。有時候,bitmap索引要快,有時候compact索引要快
  5. 索引應該創建在頻繁操作的列上
  6. 創建更多的索引,同樣也會降低查詢的性能
  7. 應該基于數據的特點,創建一種類型索引。如果這個索引能加快查詢執行
  8. 在集群資源充足的情況下,很可能沒有太大必要考慮索引

join

Hive join實現有兩種:

  1. map side join: 實現方式是replication join,把其中一個表復制到所有節點,這樣另一個表在每個節點上面的分片就可以跟這個完整的表join了
  2. reduce side join: 實現方式是repartition join,把兩份數據按照join key進行hash重分布,讓每個節點處理hash值相同的join key數據,也就是做局部的join。
  • Map join
    在hive 0.11之前,使用map join的配置方法有兩種:
    一種直接在sql中寫hint,語法是/+MAPJOIN (tbl)/,其中tbl就是你想要做replication的表。
    e.g.:
select /*+mapjoin(a) */count(*)from map_join_test ajoin map_join_test b on a.id = b.id;

另一種方法是設置 hive.auto.convert.join = true,這樣hive會自動判斷當前的join操作是否合適做map join,主要是找join的兩個表中有沒有小表。至于多大的表算小表,則是由hive.smalltable.filesize決定,默認25MB。

Before release 0.11, a MAPJOIN could be invoked either through an optimizer hint:

select /*+ MAPJOIN(time_dim) */ count(*) from
store_sales join time_dim on (ss_sold_time_sk = t_time_sk)

or via auto join conversion:

set hive.auto.convert.join=true;
select count(*) from
store_sales join time_dim on (ss_sold_time_sk = t_time_sk)

The default value for hive.auto.convert.join was false in Hive 0.10.0. Hive 0.11.0 changed the default to true (HIVE-3297). Note that hive-default.xml.template incorrectly gives the default as false in Hive 0.11.0 through 0.13.1.

MAPJOINs 把小表加載到內存中,建議內存的hashmap,然后跟大表進行穿透式key匹配。 主要實現如下的分工方式:

  • Local work:
  1. read records via standard table scan (including filters and projections) from source on local machine

  2. build hashtable in memory

  3. write hashtable to local disk

  4. upload hashtable to dfs

  5. add hashtable to distributed cache

    • Map task:
  6. read hashtable from local disk (distributed cache) into memory

  7. match records' keys against hashtable

  8. combine matches and write to output

    • No reduce task:

從Hive 0.11開始,map join 鏈會得到優化(hive.auto.convert.join=true 或者 map join hint),e.g.:

select /*+ MAPJOIN(time_dim, date_dim) */ count(*) from
store_sales 
join time_dim on (ss_sold_time_sk = t_time_sk) 
join date_dim on (ss_sold_date_sk = d_date_sk)
where t_hour = 8 and d_year = 2002

map joins優化,會嘗試合并更多Map Joins。像上例一樣,兩個表的小維度匹配部分,需要同時滿足內存要求。合并會指數的降低query的執行時間,如例中降低兩次讀取和寫入(在job之間通過HDFS通信)為一次讀取。

配置參數:

  • hive.auto.convert.join: 是否允許Hive優化普通join為map join,決定與輸入的size. (Hive 0.11 default true)
  • hive.auto.convert.join.noconditionaltask: 是否允許Hive優化普通join為map join,決定與輸入的size. 如果這個參數生效,在n-way join中n-1個表或者分區的總大小,小于hive.auto.convert.join.noconditionaltask.size, 則會直接轉化為一個map join (there is no conditional task). ( default true, Hive 0.11 中添加)
  • hive.auto.convert.join.noconditionaltask.size, 如果hive.auto.convert.join.noconditionaltask沒有開啟,這個參數不會生效,如果開啟,n-way join中n-1個表或者分區的總大小小于這個值,則這個join會直接轉化為一個map join. (there is no conditional task). (默認 10MB).

Note:
同樣的表,即使inner join能轉化為map join,outer join也不能轉化為map join,因為map join只能允許一個table(或分區)是streamed。對于full outer join是不能轉化為map join的。 對于left 或者right outer join,只有除了需要streamed的表以外的表,能滿足size配置。

Outer joins offer more challenges. Since a map-join operator can only stream one table, the streamed table needs to be the one from which all of the rows are required. For the left outer join, this is the table on the left side of the join; for the right outer join, the table on the right side, etc. This means that even though an inner join can be converted to a map-join, an outer join cannot be converted. An outer join can only be converted if the table(s) apart from the one that needs to be streamed can be fit in the size configuration. A full outer join cannot be converted to a map-join at all since both tables need to be streamed.

  • Sort-Merge-Bucket (SMB) Map join

sort-merge-bucket(SMB) join,是定義了sort和bucket表所使用的join方式。這種join方式,通過簡單的合并已經排好序的表,來讓這個join操作比普通的map join快。這就是map join優化。

參數:

set hive.auto.convert.sortmerge.join=true;
set hive.optimize.bucketmapjoin = true;
set hive.optimize.bucketmapjoin.sortedmerge = true;

hive.optimize.bucketmapjoin= true 來控制hive 執行bucket map join 了, 需要注意的是你的小表的number_buckets 必須是大表的倍數. 無論多少個表進行連接這個條件都必須滿足.(其實如果都按照2的指數倍來分bucket)。這樣數據就會按照join key做hash bucket。小表依然復制到所有節點,map join的時候,小表的每一組bucket加載成hashtable,與對應的一個大表bucket做局部join,這樣每次只需要加載部分hashtable就可以了。
set hive.optimize.bucketmapjoin.sortedmerge = true,來控制對join key上都有序的表的join優化。同時,需要設置input format,set hive.input.format = org.apache.hadoop.hive.ql.io.BucketizedHiveInputFormat

  • Left Semi Join
    舊版Hive 中沒有in/exist 這樣的子句,所以需要將這種類型的子句轉成left semi join。 left semi join 是只傳遞表的join key給map 階段 , 如果key 足夠小還是執行map join, 如果不是則還是common join。

數據傾斜

數據傾斜可能會發生在group過程和join過程。

  • Group過程
    Group過程的數據傾斜,set hive.map.aggr=true (默認開啟),在map端完成聚合,來優化傾斜。也就是在mapper內部,做部分的聚合,來輸出更少的行,減少需要排序和分發到reducer的數據量。
    Hive在嘗試做此優化,不過會判斷aggregation的效果,如果不能節省足夠的內存,就會退回標準map過程。也就是在處理了100000 行(hive.groupby.mapaggr.checkinterval 控制)后,檢查內存中的hash map的項,如果超過50%(hive.map.aggr.hash.min.reduction 控制),則認為聚合會被終止。
    Hive同樣會估計hash map中每一項所需要的內存,當使用的內存超過了mapper可用內存的50%( hive.map.aggr.hash.percentmemory 控制),則會把flush此hash map到reducers。然而這是對行數和每行大小的估計,所以如果實際值過高,可能導致還沒有flush就out of memory了。
    當出現這種OOM時,可用減少hive.map.aggr.hash.percentmemory, 但是這個對內存增長與行數無關的數據來說,不一定是有效的。這個時候,可以使用關閉以下方法,
    1. map端聚合set hive.map.aggr=false
    2. 給mapper分配更多的內存
    3. 重構query查詢。利用子查詢等方法,優化查詢語句,e.g.:
      select count(distinct v) from tbl
      改寫成
      select count(1) from (select v from tbl group by v) t.
      

Group過程傾斜,還可以開啟hive.groupby.skewindata=true來改善,這個是讓key隨機分發到reducer,而不是同樣的key分發到同一個reducer,然后reduce做聚合,做完之后再做一輪map-reduce。這個是把上面提到的map端聚合放到了reduce端,增加了reducer新的開銷,大多數情況效果并不好。

  • Join過程
    1. map join可以解決大表join小表時候的數據傾斜
    2. skew join是hive中對數據傾斜的一個解決方案,set hive.optimize.skewjoin = true;

      根據hive.skewjoin.key(默認100000)設置的數量hive可以知道超過這個值的key就是特殊key值。對于特殊的key,reduce過程直接跳過,最后再啟用新的map-reduce過程來處理。

      數據傾斜

      業務數據本身的傾斜,可以從業務數據特點本身出發,通過設置reduce數量等方式,來避免傾斜

Top N 問題

order by col limit n. hive默認的order by實現只會用1個reduce做全局排序,這在數據量大的時候job運行效率非常低。hive在0.12版本引入了parallel order by,也就是通過sampling的方式實現并行(即基于TotalOrderPartitioner)。具體開關參數是hive.optimize.sampling.orderby。但是如果使用這個參數還是很可能碰到問題的:

  • 首先如果order by字段本身取值范圍過少,會造成Split points are out of order錯誤。這是因為,假設job中reduce數量為r的話,那么TotalOrderPartitioner需要order by字段的取值至少要有r - 1個。那么這樣一來還需要關心reduce數量,增加了開發負擔,而且如果把reduce數量設的很小,優化的效果就不太明顯了。
  • 其次,設置這個參數還可能造成聚會函數出錯,這個問題只在比較新的hive版本中解決了。

sort by col limit n 可以解決top N問題,sort by保證每個reduce內數據有序,這樣就等于是做并行排序。而limit n則保證每個reduce的輸出記錄數只是n(reducer內top N)。等局部top n完成之后,再起一輪job,用1個reduce做全局top n,由于數據量大大減少單個reducer也能快速完成。

SQL整體優化

Job間并行

對于子查詢和union等情況,可以并行的執行job

set hive.exec.parallel=true, 默認的并行度為8(hive.exec.parallel. thread.number 控制),表示最多可以8個job并行,注意這個值的大小,避免占用太多資源。

減少Job數

通過優化查詢語句(SQL)來實現減少job數目(子查詢數目)的目的。

感謝大家

參考

  1. 數據倉庫中的sql性能優化(hive篇)
  2. Apache Hive
  3. Indexing in Hive
  4. Map-side aggregations in Apache Hive
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容