6、HIVE JDBC開發、UDF、體系結構、Thrift服務器、Driver、元數據庫Metastore、數據庫連接模式、單/多用戶模式、遠程服務模式、Hive技術原理解析、優化等(整理的筆記)

目錄:
5 HIVE開發
5.1 Hive JDBC開發
5.2 Hive UDF
6 Hive的體系結構
6.2 Thrift服務器
6.3 Driver
6.4 元數據庫Metastore
6.5 數據庫連接模式
6.5.1 單用戶模式
6.5.2 多用戶模式
6.5.3 遠程服務模式
7 Hive技術原理解析
7.1 Hive工作原理
7.2.1 Hive編譯器的組成
7.2.2 Query Compiler
7.2.3新版本Hive也支持Tez或Spark作爲執行引擎
8 Hive優化

5 HIVE開發

5.1 Hive JDBC開發

若想進行Hive JDBC的開發,需要開啓HiveServer2服務,這裏要參考第二章節的”連接方式”中的HiveServer2/beeline章節。

其中一個Hive JDBC的案例如下:

啓動完HiveServer2服務,就提供JDBC服務了,類似:

import java.sql.SQLException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.sql.DriverManager;
 
public class HiveJdbcClient {
  private static String driverName = "org.apache.hive.jdbc.HiveDriver";
 
  /**
   * @param args
   * @throws SQLException
   */
  public static void main(String[] args) throws SQLException {
      try {
      Class.forName(driverName);
    } catch (ClassNotFoundException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
      System.exit(1);
    }
    //replace "hive" here with the name of the user the queries should run as
    Connection con = DriverManager.getConnection("jdbc:hive2://localhost:10000/default", "hive", "");
    Statement stmt = con.createStatement();
    String tableName = "testHiveDriverTable";
    stmt.execute("drop table if exists " + tableName);
    stmt.execute("create table " + tableName + " (key int, value string)");
    // show tables
    String sql = "show tables '" + tableName + "'";
    System.out.println("Running: " + sql);
    ResultSet res = stmt.executeQuery(sql);
    if (res.next()) {
      System.out.println(res.getString(1));
    }
       // describe table
    sql = "describe " + tableName;
    System.out.println("Running: " + sql);
    res = stmt.executeQuery(sql);
    while (res.next()) {
      System.out.println(res.getString(1) + "\t" + res.getString(2));
    }
 
    // load data into table
    // NOTE: filepath has to be local to the hive server
    // NOTE: /tmp/a.txt is a ctrl-A separated file with two fields per line
    String filepath = "/tmp/a.txt";
    sql = "load data local inpath '" + filepath + "' into table " + tableName;
    System.out.println("Running: " + sql);
    stmt.execute(sql);
 
    // select * query
    sql = "select * from " + tableName;
    System.out.println("Running: " + sql);
    res = stmt.executeQuery(sql);
    while (res.next()) {
      System.out.println(String.valueOf(res.getInt(1)) + "\t" + res.getString(2));
    }
 
    // regular hive query
    sql = "select count(1) from " + tableName;
    System.out.println("Running: " + sql);
    res = stmt.executeQuery(sql);
    while (res.next()) {
      System.out.println(res.getString(1));
    }
  }
}

5.2 Hive UDF

簡單UDF示例
前期準備,要把hive的lib包導入到工程中,其中UDF依賴的是hive-exec-2.3.4.jar(針對hive2.3.4版本)。也就是說要把$HIVE_HOME\lib中的內容引入到工程中。若用到hadoop中的一些api,也把hadoop的相關的jar包也引入進去。
1、先開發一個java類,繼承UDF,並重載evaluate方法。

package hiveudf;
 
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;
 
public class ToLowerCase extends UDF {
 
   public Text evaluate(final Text s) {
      if(s == null) {returnnull;}
      returnnew Text(s.toString().toLowerCase());
   }
}

2、打成jar包上傳到服務器
3、將jar包添加到hive的classpath
4、hive> add jar /home/udf/hivedata/udf.jar;
5、hive>創建臨時函數與開發好的java class關聯

hive> create temporary function toLowercase as 'hiveudf.ToLowerCase';
OK
Time taken: 0.039 seconds
hive>

6、即可在HiveQL中使用自定義的函數toLowercase

hive> select toLowercase("ZHANGSAN") from dual;
OK
zhangsan
Time taken: 0.122 seconds, Fetched: 1 row(s)
hive>

再如判斷身份證的一個UDF函數:

package com.xxx.xxx.udf;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.hadoop.hive.ql.exec.UDF;

/**
 * 身份證校驗UDF
 * 
 * @author xxx
 * @date 2019年5月29日
 * 
 *       1.身份證號碼構成:6位地址編碼+8位生日+3位順序碼+1位校驗碼 
 *       2.驗證15位,18位證件號碼是否有效; 
 *       3.15位號碼將直接轉爲18位;【這一步不做轉化】
 *       4.校驗身份證號碼除了校驗位是否爲數值,校驗省份、出生日期
 *          省份編碼[11, 12, 13, 14, 15, 21, 22, 23, 31, 32, 33, 34, 35, 36, 37, 41, 42, 43,
                44, 45, 46, 50, 51, 52, 53, 54, 61, 62, 63, 64, 65, 71, 81, 82, 91]
 *       5.校驗位不正確的會被正確的替代.【最後1位是根據前面17位生成的,如果不正確,會生成正確的檢驗碼進行修正.】
 *       6.出生日期邏輯有效性,即是否1864年前出生,是否當前日期後出生已校驗。
 *       7.1984年4月6日國務院發佈《中華人民共和國居民身份證試行條例》,並且開始頒發第一代居民身份證,按壽命120歲驗證,年齡區間是1864年至今。 
 *       8.15位證件號碼轉換未考慮1900年前出生的人【這一步不做轉化】
 *       9.去除null,去除空串,去除非15位和18位
 *       10.判斷前17位是否含非數字字符.
 *       11.判斷非法日期,如生日所在日期(2019年2月29日)不存在.
 * 
 *  add jar hdfs://tqHadoopCluster/xxx/udf/idCardUdf.jar;
 *  create temporary function cheakIdCardNumber as 'com.xxx.xxx.udf.IdNumber';
 *  這這就可以使用cheakIdCardNumber(String idCardNumber)進行校驗身份證了
 */
public class IdNumber extends UDF {
    private final static int NEW_CARD_NUMBER_LENGTH = 18;
    private final static int OLD_CARD_NUMBER_LENGTH = 15;

    /** 開始年份 */
    private final static int YEAR_BEGIN = 1864;

    /** 18位身份證中最後一位校驗碼 */
    private final static char[] VERIFY_CODE = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'};
    
    /** 18位身份證中,各個數字的生成校驗碼時的權值 */
    private final static int[] VERIFY_CODE_WEIGHT = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
    
    /** 省份編碼集合 */
    private final static int[] PROVINCE_CODE = {11, 12, 13, 14, 15, 21, 22, 23, 31, 32, 33, 34, 35, 36, 37, 41, 42, 43,
        44, 45, 46, 50, 51, 52, 53, 54, 61, 62, 63, 64, 65, 71, 81, 82, 91};
    
    private final static Pattern PATTERN = Pattern.compile("[0-9]*");
    
    private final static ThreadLocal<SimpleDateFormat> THREADLOCAL = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            // 直接返回null
            return null;
        }
    };
    /*
    public static void main(String[] args) {
        IdNumber idNumber = new IdNumber();
        //String evaluate = idNumber.evaluate("125197791116622");
        // 125197  791116622
        // 12519719791116622X
        // 330304198705234817
        String evaluate = idNumber.evaluate("330304201902284815");
        System.out.println(evaluate);
    }
    */
    public String evaluate(String cardNumber) {
        // 1.去除NULL
        if (cardNumber == null) {
            return null;
        } 
        Date dNow = new Date();
        SimpleDateFormat ft = new SimpleDateFormat("yyyy");
        int yearEnd = Integer.parseInt(ft.format(dNow));
        cardNumber = cardNumber.trim();
        
        // 2.去除空字符串
        if (cardNumber.length() == 0) {
            return null;
        }
        
        // 3.去除長度不正確的,非15或18位
        if (NEW_CARD_NUMBER_LENGTH != cardNumber.length() & OLD_CARD_NUMBER_LENGTH != cardNumber.length()) {
            return null;
        }
        
        // 4.如果是18位
        if (NEW_CARD_NUMBER_LENGTH == cardNumber.length()) {
            
            // 4.1.前17位不爲數值,返回null
            if (isNumeric(cardNumber.substring(0, 17)) == false) {
                return null;
            }
            
            // 校驗位修正,判斷最後一位是否正確
            if (IdNumber.calculateVerifyCode(cardNumber) != cardNumber.charAt(17)) {
                //如果最後一位校驗碼不會,則修正校驗碼.
                cardNumber = contertToNewCardNumber(cardNumber, NEW_CARD_NUMBER_LENGTH);
            }
            
            // 身份證前2位是省份編碼, 如果編碼不在已知集合中, 返回null.
            if (isProvince(cardNumber.substring(0, 2)) == false) {
                return null;
            } 
            
            // 非法日期轉換前後不一致, 如當前年份不存在2月29日, 返回null
            if (isDate(cardNumber.substring(6, 14)) == false) {
                return null;
            }
            
            // 1984年4月6日國務院發佈《中華人民共和國居民身份證試行條例》,並且開始頒發第一代居民身份證,按壽命120歲驗證,年齡區間是1864年至今。 小於1984的爲非法.
            int birthdayYear = Integer.parseInt(cardNumber.substring(6, 10));
            if (birthdayYear < YEAR_BEGIN) {
                return null;
            }
            
            // 如果出生年月大於當前日期, 返回null
            if (birthdayYear > yearEnd) {
                return null;
            }
        }
        
        // 5.如果是15位:15位身份證號碼不轉換爲18位.
        if (OLD_CARD_NUMBER_LENGTH == cardNumber.length()) {
            // 前15位中含非數字字符, 返回null
            if (isNumeric(cardNumber) == false) {
                return null;
            }

            // 省份編碼不在在已知集合中, 返回null
            if (isProvince(cardNumber.substring(0, 2)) == false) {
                return null;
            }
            
            // 非法日期轉換前後不一致, 返回null
            if (isDate("19" + cardNumber.substring(6, 12)) == false) {
                return null;
            }
            
            //將15位身份證轉化爲18位,考慮1900年前出生的人。
            //cardNumber = contertToNewCardNumber(cardNumber, OLD_CARD_NUMBER_LENGTH);
        }
        
        return cardNumber;
    }

    /**
     * 校驗碼(第十八位數):
     * 
     * 十七位數字本體碼加權求和公式 S = Sum(Ai * Wi), i = 0...16 ,先對前17位數字的權求和; 
     * Ai:表示第i位置上的身份證號碼數字值 
     * Wi:表示第i位置上的加權因子 
     * Wi: 7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2; 
     * 計算模 Y = mod(S, 11) 通過模得到對應的校驗碼
     *  Y: 0 1 2 3 4 5 6 7 8 9 10 
     *  校驗碼: 1 0 X 9 8 7 6 5 4 3 2
     * 
     * @param cardNumber
     * @return
     */
    private static char calculateVerifyCode(CharSequence cardNumber) {
        int sum = 0;
        for (int i = 0; i < NEW_CARD_NUMBER_LENGTH - 1; i++) {
            char ch = cardNumber.charAt(i);
            sum += ((int)(ch - '0')) * VERIFY_CODE_WEIGHT[i];
        }
        //VERIFY_CODE = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'};
        return VERIFY_CODE[sum % 11];
    }

    /**
     * A-把15位身份證號碼轉換到18位身份證號碼 15位身份證號碼與18位身份證號碼的區別爲: 
     * 1. 15位身份證號碼中,"出生年份"字段是2位,轉換時需要補入"19",表示20世紀
     * 2. 15位身份證無最後一位校驗碼。18位身份證中,校驗碼根據根據前17位生成 B-18位身份證號碼校驗位修復
     * 
     * @param cardNumber
     * @return
     */
    private static String contertToNewCardNumber(String idCardNumber, int cardNumberLength) {
        StringBuilder buf = new StringBuilder(NEW_CARD_NUMBER_LENGTH);
        if (cardNumberLength == NEW_CARD_NUMBER_LENGTH) {
            buf.append(idCardNumber.substring(0, 17));
            buf.append(IdNumber.calculateVerifyCode(buf));
        } else if (cardNumberLength == OLD_CARD_NUMBER_LENGTH) {
            buf.append(idCardNumber.substring(0, 6));
            buf.append("19");
            buf.append(idCardNumber.substring(6));
            buf.append(IdNumber.calculateVerifyCode(buf));
        }
        return buf.toString();
    }

    /**
     * 功能:判斷字符串是否爲數字
     * 
     * @param str
     * @return
     */
    private static boolean isNumeric(String str) {
        Matcher isNum = PATTERN.matcher(str);
        if (isNum.matches()) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 功能:判斷前兩位是在省份編碼集合內
     * @param province
     * @return
     */
    private static boolean isProvince(String province) {
        int prov = Integer.parseInt(province);
        for (int i = 0; i < PROVINCE_CODE.length; i++) {
            if (PROVINCE_CODE[i] == prov) {
                return true;
            }
        }
        return false;
    }

    public static SimpleDateFormat getDateFormat() {
        SimpleDateFormat df = (SimpleDateFormat)THREADLOCAL.get();
        if (df == null) {
            df = new SimpleDateFormat("yyyyMMdd");
            df.setLenient(false);
            THREADLOCAL.set(df);
        }
        return df;
    }

    /**
     * 功能:判斷出生日期字段是否爲日期
     * 
     * @param str
     * @return
     */
    private static boolean isDate(String birthDate) {
        try {
            SimpleDateFormat sdf = getDateFormat();
            //如果不合法,會拋異常, Unparseable date: "20190229"
            Date cacheBirthDate = sdf.parse(birthDate);
            if (sdf.format(cacheBirthDate).equals(birthDate) == false) {
                // 非法日期轉換前後不一致
                return false;
            }
        } catch (Exception e) {
            //e.printStackTrace();
            return false;
        }
        return true;
    }
}

6 Hive的體系結構

下面是Hive的架構圖:
在這裏插入圖片描述
圖 1 Hive體系結構

6.1用戶接口

(1)、CLI:CLI啓動的時候,會同時啓動一個Hive副本。
(2)、JDBC客戶端:封裝了Thrift,java應用程序,可以通過指定的主機和端口連接到在另外一個進程中運行的hive服務器。
(3)、ODBC客戶端:ODBC驅動允許支持ODBC協議的應用程序連接到Hive
(4)、WUI接口:通過瀏覽器訪問Hive

6.2Thrift服務器

基於socket通訊,支持跨語言。Hive Thrift服務簡化了在多編程語言中運行hive的命令。綁定支持C++,Java,PHP,Python和Ruby語言。

6.3 Driver

核心組件,整個Hive的核心,該組件包括Complier、Optimizer和Executor; 解釋器、編譯器、優化器完成HQL查詢語句從詞法分析、語法分析、編譯、優化以及查詢計劃的生成。生成的查詢計劃存儲在HDFS中,並在隨後有MapReduce調用執行。

6.4 元數據庫Metastore

Hive的數據由兩部分組成:數據文件和元數據。元數據用於存放Hive庫的基礎信息,它存儲在關係數據庫中,例如mysql、Oracle、derby。元數據包括:數據庫信息、表達的名字,表的列和分區及其屬性,表的屬性,表的數據所在目錄等。
Hadoop
Hive的數據文件存儲在HDFS中,大部分的查詢由MapReduce完成。(對於包含*的查詢,比如select * from tbl不會生成MapReduce作業)

6.5數據庫連接模式

6.5.1 單用戶模式

此模式連接到一個In-memory的數據庫Derby,Derby數據是Hive自帶的數據庫,默認只能有一個連接,用戶連接到Hive後,會在當前目錄生成一個metastore文件來存放元數據信息,不同的用戶連接到Hive看到的信息不一致。

6.5.2多用戶模式

通過網絡連接到一個關係型數據庫中,如MySQL,是最進程使用到的模式。這樣多個用戶操作的元數據信息統一放到關係型數據庫,這樣不同用戶可以操作相同的對象。

6.5.3 遠程服務模式

用於非Java客戶端訪問元數據,在服務器啓動MetaStoreServer,客戶端利用Thrift協議通過MetaStoreServer訪問元數據。

7 Hive技術原理解析

7.1 Hive工作原理

如下圖所示:
在這裏插入圖片描述

流程大致步驟爲:

  1. 用戶提交查詢等任務給Driver。
  2. 編譯器獲得該用戶的任務Plan。
  3. 編譯器Compiler根據用戶任務去MetaStore中獲取需要的Hive的元數據信息。
  4. 編譯器Compiler得到元數據信息,對任務進行編譯,先將HiveQL轉換爲抽象語法樹,然後將抽象語法樹轉換成查詢塊,將查詢塊轉化爲邏輯的查詢計劃,重寫邏輯查詢計劃,將邏輯計劃轉化爲物理的計劃(MapReduce), 最後選擇最佳的策略。
  5. 將最終的計劃提交給Driver。
  6. Driver將計劃Plan轉交給ExecutionEngine去執行,獲取元數據信息,提交給JobTracker或者SourceManager執行該任務,任務會直接讀取HDFS中文件進行相應的操作。
  7. 獲取執行的結果。
  8. 取得並返回執行結果。

創建表時:
1、解析用戶提交的Hive語句對其進行解析分解爲表、字段、分區等Hive對象。根據解析到的信息構建對應的表、字段、分區等對象,從SEQUENCE_TABLE中獲取構建對象的最新的ID,與構建對象信息(名稱、類型等等)一同通過DAO方法寫入元數據庫的表中,成功後將SEQUENCE_TABLE中對應的最新ID+5.實際上常見的RDBMS都是通過這種方法進行組織的,其系統表中和Hive元素一樣顯示了這些ID信息。通過這些元數據可以很容易的讀取到數據。

工作原理的另外一種說法是:
接收到一個sql,後面做的事情包括:
1.詞法分析/語法分析
使用antlr將SQL語句解析成抽象語法樹-AST
2.語義分析
從Megastore獲取模式信息,驗證SQL語句中隊表名,列名,以及數據類型的檢查和隱式轉換,以及Hive提供的函數和用戶自定義的函數(UDF/UAF)
3.邏輯計劃生產
生成邏輯計劃-算子樹
4.邏輯計劃優化
對算子樹進行優化,包括列剪枝,分區剪枝,謂詞下推等
5.物理計劃生成
將邏輯計劃生產包含由MapReduce任務組成的DAG的物理計劃
6.物理計劃執行
將DAG發送到Hadoop集羣進行執行
7.將查詢結果返回

流程如下圖:
在這裏插入圖片描述

7.2 Hive編譯過程

轉化過程:
基本流程爲:將HiveQL轉化爲”抽象語法樹”—>”查詢塊”—>”邏輯查詢計劃””物理查詢計劃”最終選擇最佳決策的過程。

優化器的主要功能:

  1. 將多Multiple join 合併爲一個Muti-way join
  2. 對join、group-by和自定義的MapReduce操作重新進行劃分。
  3. 消減不必要的列。
  4. 在表的掃描操作中推行使用斷言。
  5. 對於已分區的表,消減不必要的分區。
  6. 在抽樣查詢中,消減不必要的桶。
  7. 優化器還增加了局部聚合操作用於處理大分組聚合和增加再分區操作用於處理不對稱的分組聚合。

7.2.1 Hive編譯器的組成

在這裏插入圖片描述
對於此的說明,網絡上的另外一種說明:
在這裏插入圖片描述
在這裏插入圖片描述

7.2.2 Query Compiler

在這裏插入圖片描述
網絡上描述上述過程的另外一張圖(同樣是Query Compiler過程的另外一種畫法):
在這裏插入圖片描述

爲了說明上述執行流程,需要輔助一個簡單的Hive查詢例子:

案例1-1:Hive執行以下語句:

INSERT OVERWRITE TABLE access_log_temp2
SELECT a.user, a.prono, a.maker, a.price FROM access_log_hbase a JOIN product_hbase p 
ON (a.prono = p.prono)

具體執行流程:
(1)Parser生成AST
在這裏插入圖片描述
HQL生成的語法解析樹(AST):
HIVE主要是利用UNDERLER處理得到上面的抽象語法樹(這棵抽象語法樹主要是根據underller的分詞規則來確定根結點和左右孩子的,Hive的這部分處理是利用underller進行的)。
(2)Semantic Analyzer生成Query Block(QB)
在這裏插入圖片描述

Tip:
第二步會將抽象語法樹按照QB的方式轉換爲由QB組成的樹。通常情況下,每一個From子句會生成一個QB(Query Block)。通過查看具體有多少個QB就可查看Hive sql語句最終優化成多少個MapReduce的作業。

QB是由兩個子數據結構組成即MetaData(元數據信息)和ParseInfo。語義分析的主要過程是向QB中填充元數據,另一個過程則是摘取AST的子節點,存儲在ParseInfo中。Semantic Analyzer過程主要是經過以上兩個過程,然後把QB關聯起來形成一個由QB構成的圖.

Logical Plan generator會將上一步生成的Query Block按照前文講的幾種的Operator,轉換爲Operator Tree。
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
由於Hive查詢語句會將最後的查詢結果寫入到表access_log_temp2中,所以QB的MetaData中的Name To Destination…會轉化爲Operator Tree中的FileSinkOperator(文件下沉)。
最後會生成如下的OP Tree:

Logical Plan Generator (Result)
在這裏插入圖片描述

這裏就得到了一個有向無環圖(DAG),當在傳統數據庫中會對上圖進行優化然後執行;而在Hadoop集羣中這張圖是不行的,因爲在集羣中執行需要對這張圖進行優化、切片,分佈式化轉化爲MapReduce作業然後執行。

7.2.3新版本Hive也支持Tez或Spark作爲執行引擎:

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

物理計劃可以通過hive的Explain命令輸出:
例如:

0: jdbc:hive2://master:10000/dbmfz> explain select count(*) from record_dimension;
+------------------------------------------------------------------------------------------------------+--+
|                                               Explain                                                |
+------------------------------------------------------------------------------------------------------+--+
| STAGE DEPENDENCIES:                                                                                  |
|   Stage-1 is a root stage                                                                            |
|   Stage-0 depends on stages: Stage-1                                                                 |
|                                                                                                      |
| STAGE PLANS:                                                                                         |
|   Stage: Stage-1                                                                                     |
|     Map Reduce                                                                                       |
|       Map Operator Tree:                                                                             |
|           TableScan                                                                                  |
|             alias: record_dimension                                                                  |
|             Statistics: Num rows: 1 Data size: 543 Basic stats: COMPLETE Column stats: COMPLETE      |
|             Select Operator                                                                          |
|               Statistics: Num rows: 1 Data size: 543 Basic stats: COMPLETE Column stats: COMPLETE    |
|               Group By Operator                                                                      |
|                 aggregations: count()                                                                |
|                 mode: hash                                                                           |
|                 outputColumnNames: _col0                                                             |
|                 Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE Column stats: COMPLETE    |
|                 Reduce Output Operator                                                               |
|                   sort order:                                                                        |
|                   Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE Column stats: COMPLETE  |
|                   value expressions: _col0 (type: bigint)                                            |
|       Reduce Operator Tree:                                                                          |
|         Group By Operator                                                                            |
|           aggregations: count(VALUE._col0)                                                           |
|           mode: mergepartial                                                                         |
|           outputColumnNames: _col0                                                                   |
|           Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE Column stats: COMPLETE          |
|           File Output Operator                                                                       |
|             compressed: false                                                                        |
|             Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE Column stats: COMPLETE        |
|             table:                                                                                   |
|                 input format: org.apache.hadoop.mapred.SequenceFileInputFormat                       |
|                 output format: org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormat             |
|                 serde: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe                            |
|                                                                                                      |
|   Stage: Stage-0                                                                                     |
|     Fetch Operator                                                                                   |
|       limit: -1                                                                                      |
|       Processor Tree:                                                                                |
|         ListSink                                                                                     |
|                                                                                                      |
+------------------------------------------------------------------------------------------------------+--+
rows selected (0.844 seconds)

8 Hive優化

1、join連接時的優化:當三個或多個以上的表進行join操作時,如果每個on使用相同的字段連接時只會產生一個mapreduce。
2、join連接時的優化:當多個表進行查詢時,從左到右表的大小順序應該是從小到大。原因:hive在對每行記錄操作時會把其他表先緩存起來,直到掃描最後的表進行計算
3、在where字句中增加分區過濾器。
4、當可以使用left semi join 語法時不要使用inner join,前者效率更高。原因:對於左表中指定的一條記錄,一旦在右表中找到立即停止掃描。
5、如果所有表中有一張表足夠小,則可置於內存中,這樣在和其他表進行連接的時候就能完成匹配,省略掉reduce過程。設置屬性即可實現,set hive.auto.covert.join=true; 用戶可以配置希望被優化的小表的大小 set hive.mapjoin.smalltable.size=2500000; 如果需要使用這兩個配置可置入$HOME/.hiverc文件中。
6、同一種數據的多種處理:從一個數據源產生的多個數據聚合,無需每次聚合都需要重新掃描一次。
例如:insert overwrite table student select * from employee; insert overwrite table person select * from employee;
可以優化成:from employee insert overwrite table student select * insert overwrite table person select *
7、limit調優:limit語句通常是執行整個語句後返回部分結果。set hive.limit.optimize.enable=true;
8、開啓併發執行。某個job任務中可能包含衆多的階段,其中某些階段沒有依賴關係可以併發執行,開啓併發執行後job任務可以更快的完成。設置屬性:set hive.exec.parallel=true;
9、hive提供的嚴格模式,禁止3種情況下的查詢模式。
a:當表爲分區表時,where字句後沒有分區字段和限制時,不允許執行。
b:當使用order by語句時,必須使用limit字段,因爲order by 只會產生一個reduce任務。
c:限制笛卡爾積的查詢。
10、合理的設置map和reduce數量。
11、jvm重用。可在hadoop的mapred-site.xml中設置jvm被重用的次數。

打個賞唄,您的支持是我堅持寫好博文的動力
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章