1. 程式人生 > >Hive 通過關閉CBO (Cost based Optimizer) 來優化特定的SQL執行

Hive 通過關閉CBO (Cost based Optimizer) 來優化特定的SQL執行

Hive 自0.14.0開始,加入了一項”Cost based Optimizer”來對HQL執行計劃進行優化,這個功能通過”hive.cbo.enable”來開啟。在Hive 1.1.0之後,這個feature是預設開啟的,它可以自動優化HQL中多個JOIN的順序,並選擇合適的JOIN演算法

Join reordering and join algorithm selection are few of the optimizations that can benefit from a cost based optimizer. Cost based optimizer would free up user from having to rearrange joins in the right order or from having to specify join algorithm by using query hints and configuration options. This can potentially free up users to model their reporting and ETL needs close to business process without having to worry about query optimizations.

不過在工作中,發現這個功能在某些情況下反而會導致Query的執行效率降低,這裡寫個blog記錄一下。以下僅針對Hive on tez , 至於 Hive on mapreduce 的情況待考察。

假如我們有三張表:
1. 事實表 table_fact

col_name data_type comment
field_dim_1 string
field_dim_2 string
score bigint

2. 維度表 table_dim1

col_name data_type comment
field_dim_1 string
field_res_1 string

3. 維度表 table_dim2

col_name data_type comment
field_dim_1 string
field_dim_2 string
field_res_2 bigint

這是一個常見的星型模型的縮影,由一個事實表和兩個維度表組成。通常我們在查詢時需要從維度表中關聯出我們需要的資訊,比如:

select 
    d1.field_res_1,
    d2.field_res_2,
    sum(f.score) 
from 
    table_fact f 
left join table_dim_1 d1 on f.field_dim_1=d1.field_dim_1 
left join table_dim_2 d2 on f.field_dim_1=d2.field_dim_1 and f.field_dim_2=d2.field_dim_2 
where 
    f.field_dim_2="abc" 
group by 
    d1.field_res_1,
    d2.field_res_2;

這是一個比較常見的查詢語句。在cbo開關開啟的情況下,執行計劃是這個樣子的:

+----------------------------------------------------+
|                      Explain                       |
+----------------------------------------------------+
| Plan optimized by CBO.                             |
|                                                    |
| Vertex dependency in root stage                    |
| Reducer 2 <- Map 1 (SIMPLE_EDGE), Map 4 (SIMPLE_EDGE), Map 5 (SIMPLE_EDGE) |
| Reducer 3 <- Reducer 2 (SIMPLE_EDGE)               |
|                                                    |
| Stage-0                                            |
|   Fetch Operator                                   |
|     limit:-1                                       |
|     Stage-1                                        |
|       Reducer 3                                    |
|       File Output Operator [FS_17]                 |
|         Group By Operator [GBY_15] (rows=1 width=0) |
|           Output:["_col0","_col1","_col2"],aggregations:["sum(VALUE._col0)"],keys:KEY._col0, KEY._col1 |
|         <-Reducer 2 [SIMPLE_EDGE]                  |
|           SHUFFLE [RS_14]                          |
|             PartitionCols:_col0, _col1             |
|             Group By Operator [GBY_13] (rows=2 width=0) |
|               Output:["_col0","_col1","_col2"],aggregations:["sum(_col2)"],keys:_col4, _col7 |
|               Select Operator [SEL_12] (rows=2 width=0) |
|                 Output:["_col4","_col7","_col2"]   |
|                 Merge Join Operator [MERGEJOIN_23] (rows=2 width=0) |
|                   Conds:RS_8._col0=RS_9._col0(Left Outer),RS_8._col0=RS_10._col0(Left Outer),Output:["_col2","_col4","_col7"] |
|                 <-Map 1 [SIMPLE_EDGE]              |
|                   SHUFFLE [RS_8]                   |
|                     PartitionCols:_col0            |
|                     Select Operator [SEL_2] (rows=1 width=0) |
|                       Output:["_col0","_col2"]     |
|                       Filter Operator [FIL_20] (rows=1 width=0) |
|                         predicate:(field_dim_2 = 'abc') |
|                         TableScan [TS_0] (rows=1 width=0) |
|                           [email protected]_fact,f,Tbl:PARTIAL,Col:NONE,Output:["field_dim_1","field_dim_2","score"] |
|                 <-Map 4 [SIMPLE_EDGE]              |
|                   SHUFFLE [RS_9]                   |
|                     PartitionCols:_col0            |
|                     Select Operator [SEL_4] (rows=1 width=0) |
|                       Output:["_col0","_col1"]     |
|                       TableScan [TS_3] (rows=1 width=0) |
|                         [email protected]_dim_1,d1,Tbl:PARTIAL,Col:NONE,Output:["field_dim_1","field_res_1"] |
|                 <-Map 5 [SIMPLE_EDGE]              |
|                   SHUFFLE [RS_10]                  |
|                     PartitionCols:_col0            |
|                     Select Operator [SEL_7] (rows=1 width=0) |
|                       Output:["_col0","_col2"]     |
|                       Filter Operator [FIL_22] (rows=1 width=0) |
|                         predicate:('abc' = field_dim_2) |
|                         TableScan [TS_5] (rows=1 width=0) |
|                           [email protected]_dim_2,d2,Tbl:PARTIAL,Col:NONE,Output:["field_dim_1","field_dim_2","field_res_2"] |
|                                                    |
+----------------------------------------------------+

我們可以看到,line 4提示這個查詢經過了CBO的優化。hive對這個SQL做了一系列的優化(不僅僅是CBO的優化):

  • filter的下推(line 33,49)
  • projection的下推(line 31,40,47)
  • 對join key的優化 (line 26等)
  • 儘可能的合併了MR任務(line 7、8)

其他的優化不一一列舉了。這個執行計劃中將2個join用一次map-reduce就解決了,最後一個Reduce-3是為了做group by的。

但是如果事實表 table_fact 中的欄位 field_dim_1 存在資料傾斜的話會怎麼樣呢?我們可以看到,在這個執行計劃中,shuffle的key都是針對 field_dim_1的,如果這個欄位存在大量的資料傾斜的話,那麼這個查詢會執行的非常緩慢。

針對資料傾斜,我們通常的解決方法無外乎就是 mapjoin、 salting ,或者做任務的拆分等幾種方法。這裡的查詢是一張較大的事實表和兩張維度表進行的關聯。如果維度表(或者經過filter && projection)之後的維度表足夠小的話,那麼這裡實際上hive是可以自動將這個查詢轉化為 map side join 的。

由於我為了說明這個情景,以上的三張表建立的都是空表,所以理論上hive應該會把這個查詢轉化為 map side join 。但是為什麼沒有呢? 這就是cbo的關係了。

+----------------------------------------------------+
|                      Explain                       |
+----------------------------------------------------+
| Vertex dependency in root stage                    |
| Map 1 <- Map 3 (BROADCAST_EDGE), Map 4 (BROADCAST_EDGE) |
| Reducer 2 <- Map 1 (SIMPLE_EDGE)                   |
|                                                    |
| Stage-0                                            |
|   Fetch Operator                                   |
|     limit:-1                                       |
|     Stage-1                                        |
|       Reducer 2                                    |
|       File Output Operator [FS_15]                 |
|         Group By Operator [GBY_13] (rows=1 width=0) |
|           Output:["_col0","_col1","_col2"],aggregations:["sum(VALUE._col0)"],keys:KEY._col0, KEY._col1 |
|         <-Map 1 [SIMPLE_EDGE]                      |
|           SHUFFLE [RS_12]                          |
|             PartitionCols:_col0, _col1             |
|             Group By Operator [GBY_11] (rows=1 width=0) |
|               Output:["_col0","_col1","_col2"],aggregations:["sum(_col2)"],keys:_col7, _col13 |
|               Select Operator [SEL_10] (rows=1 width=0) |
|                 Output:["_col7","_col13","_col2"]  |
|                 Map Join Operator [MAPJOIN_22] (rows=1 width=0) |
|                   Conds:MAPJOIN_21._col0, _col1=RS_7.field_dim_1, field_dim_2(Left Outer),HybridGraceHashJoin:true,Output:["_col2","_col7","_col13"] |
|                 <-Map 4 [BROADCAST_EDGE]           |
|                   BROADCAST [RS_7]                 |
|                     PartitionCols:field_dim_1, field_dim_2 |
|                     Filter Operator [FIL_20] (rows=1 width=0) |
|                       predicate:(field_dim_2 = 'abc') |
|                       TableScan [TS_2] (rows=1 width=0) |
|                         [email protected]_dim_2,d2,Tbl:PARTIAL,Col:NONE,Output:["field_dim_1","field_dim_2","field_res_2"] |
|                 <-Map Join Operator [MAPJOIN_21] (rows=1 width=0) |
|                     Conds:FIL_18.field_dim_1=RS_4.field_dim_1(Left Outer),HybridGraceHashJoin:true,Output:["_col0","_col1","_col2","_col7"] |
|                   <-Map 3 [BROADCAST_EDGE]         |
|                     BROADCAST [RS_4]               |
|                       PartitionCols:field_dim_1    |
|                       TableScan [TS_1] (rows=1 width=0) |
|                         [email protected]_dim_1,d1,Tbl:PARTIAL,Col:NONE,Output:["field_dim_1","field_res_1"] |
|                   <-Filter Operator [FIL_18] (rows=1 width=0) |
|                       predicate:(field_dim_2 = 'abc') |
|                       TableScan [TS_0] (rows=1 width=0) |
|                         [email protected]_fact,f,Tbl:PARTIAL,Col:NONE,Output:["field_dim_1","field_dim_2","score"] |
|                                                    |
+----------------------------------------------------+

首先CBO優化的標誌不見了(line 4),然後 Stage1 針對兩個維表的 Map 操作變成了 BROADCAST (line 5) ,並且輸出結果傳給了針對事實表的 Map .

由此可以看到關閉CBO之後, Hive會使用map join來執行這個查詢,另外整個查詢的shuffle次數還從2次減少到了1次,即使在沒有資料傾斜的情況下,查詢效率也很可能會快很多。

工作中實際查詢的SQL很複雜,就不貼了。不過查詢瓶頸最後抽象出來基本就是上面的情形,而且關聯欄位的某個值傾斜非常嚴重,整個查詢持續了數個小時也沒有會結束的跡象。當我們把CBO關閉以後,Hive將這個查詢優化為了 Map Side Join,很快就返回了結果。

由此可見,一些優化的開關也未必是開啟就好,小心過度優化

相關推薦

Hive 通過關閉CBO (Cost based Optimizer) 優化特定SQL執行

Hive 自0.14.0開始,加入了一項”Cost based Optimizer”來對HQL執行計劃進行優化,這個功能通過”hive.cbo.enable”來開啟。在Hive 1.1.0之後,這個feature是預設開啟的,它可以自動優化HQL中多個JOIN的

深入淺出Calcite與SQL CBOCost-Based Optimizer優化

[toc] 前陣子工作上需要用到Calcite做一些事情,然後發現這個東西也是蠻有意思的,就花了些時間研究了一下。本篇主要圍繞SQL 優化這塊來介紹Calcite,後面會介紹Hive如何Calcite進行SQL的優化。 此外,也將Calcite的一些使用樣例整理成到github,[https://gith

記錄linux下通過對limits的設置優化系統性能

pgrep 目前 接下來 total 滿足 執行 獲取 linux下 第一個 系統中子進程繼承父進程的系統限制。只有以root用戶運行的進程能任意修改限制。其它進程不能增加硬限制值。這樣在一個session中登錄進程設置的硬限制值影響該session中的所有進程。 當要優化

mysql通過將or改成union優化sql效能問題一例

某系統測試環境有支SQL執行時間較長,開發人員請求dba協助優化。 原SQL如下: SELECT   g.id,           ----省略-----     FROM    g,             y,             t,             o

explain 了解SQL執行的狀態

explain來了解SQL執行的狀態。 explain select * from wp_posts\G; explain顯示了mysql如何使用索引來處理select語句以及連線表。可以幫助選擇更好的索引和寫出更優化的查詢語句。 使用方法,在select語句前加上e

通過python的urllib.request庫爬取一只貓

com cat alt cnblogs write amazon 技術分享 color lac 我們實驗的網站很簡單,就是一個關於貓的圖片的網站:http://placekitten.com 代碼如下: import urllib.request respond =

[性能調優]如何通過讀PeopleSoft Trace文件調優

正在 內容 準備 .cn 技術分享 分享 需要 pps ima 理解PeopleSoft Trace文件對於解決性能問題是絕對有必要的。你可能面臨一個問題,用戶抱怨性能較慢,而OEM並沒有補貨SQL,你有2種方法選擇:使用PeopleSoft trace檢查或啟用數據庫的跟

【VB.NET】通過 IPIP.NET 數據庫查詢IP地址

exit utf try 付費 utf8 地址 .com href 使用方法 上一次介紹了利用純真數據庫查詢IP地址詳細信息的方法。然而純真數據庫是由網友反饋所提供的,很多數據描述並不準確,所以我上網找了一些其他的IP數據庫,最後就找到了 ipip.net 這個網站所提供的

ireport報表制作, 通過節點、產品類型判斷,當該節點審核通過之後,報表相對應的字段出顯示審核意見及簽名

not body node 代碼 person images bst nature sysdate 1、代碼 (與本內容相關的代碼:7~36) 1 select so.sale_order_no as sale_order_no, 2 (SELECT company_

MySQL-5.6.34通過show global status like 查看sql語句的執行情

cal erro optimize col resign relay trigge log sql語句 需求 老大:zain啊,咱們的數據庫今天有多少查詢語句啊?我 :額,稍等,我看看啊; 心想,{尼瑪,我怎麽知道有多少select語句啊} 那麽問題來了,

通過腳本調用mysql啟動keepalived的配置的VIP

fail sta top usr star -h mysql log ucc mysql [root@mariadb01 ~]# cat check_mysql.sh MYSQL=/usr/bin/mysql MYSQL_HOST=localhost MYSQL_USE

通過生成支付二維碼實現微信支付的解決方案 - EasyWechat版(轉)

ppi ppk 一個 segment url out -s easy 記得 上一篇我們講了在微信瀏覽器內實現微信支付的功能,它特別適合於一些基於微信公眾號的h5站點等,支付流程也相當流暢,但是... 還有一種情況,比如現在北哥兄弟連PC版,是生成了一個二維碼,這個二維碼

通過反編譯字節碼理解 Java 枚舉

enum枚舉的聲明很簡單, 像 enum Gender { Male, Female }, 其余事情就是 Java 編譯器幫我們幹的了,所以 enum 也就是一塊語法糖。有了枚舉確實是很方便,避免了傳統常量的無範圍性。那麽編譯器到底在後面做了什麽呢?以及理解了這個之後我們可以怎麽去使用 Java 的枚舉, 下

全網最詳細的hive-site.xml配置文件裏如何添加達到Hive與HBase的集成,即Hive通過這些參數去連接HBase(圖文詳解)

out 開源精神 http FN image ava ext 必須 .cn   不多說,直接上幹貨!   一般,普通的情況是    <configuration>   <property>   

使用ioc創建對象的方式之一(通過無參的構造方法創建:)

imp ext test code 創建對象 .sh sta [] spring 通過無參的構造方法來創建: User.java 1 package com.yikuan.vo; 2 3 public class User { 4 public User

C# 通過 Observer觀察者 設計模式 理解 抽象類 和 介面 應用在什麼地方

什麼時候用抽象類?什麼時候用介面?怎麼理解抽象類?怎麼理解介面? 一、百度解釋抽象類和介面的區別 總而言之就是一句話:抽象類可以包含具體實現,介面只能包含定義。 實現介面時必須實現介面定義的方法等,抽象類中如果給方法加上了“abstract”,那麼這個方法也需要在繼承後實現這個

通過連線大華dss平臺實現檢視攝像頭畫面和實現雲臺控制功能Extjs

上個版本的大華攝像頭監控的檢視雖然可以達到初步效果,但是後續實現雲臺的控制等就無法實現了,所以後來採取了通過連線大華的dss平臺來實現監控畫面的檢視和雲臺控制,實際的實現其實就是呼叫大華dss平臺的介面,來實現功能。 程式碼實現:   Ext.onReady(function

Linux Centos7通過shell指令碼監控mysql的執行狀態

vim checkmysql.sh #!/bin/sh #create by mingongge at 2018-10-10 port=`netstat -lnt|grep 3306|wc -l` if  [ $post -ne 1 ] ;then    now

通過設定ie的通過跨域訪問資料來源,訪問本地服務

1、首先設定通過域訪問資料來源 設定通過域訪問資料來源 2、javascript指令碼ajax使用本地服務登入(評價,人證的類似)介面 <html> <head> <script type="text/javascript"> f

通過修改mysql配置優化效能

innodb_flush_log_at_trx_commit和sync_binlog是MySQL innodb引擎的兩個重要的引數,其中innodb_flush_log_at_trx_commit是將事務日誌從innodb log buffer寫入到redo log中,sync_binlog