1. 程式人生 > 其它 >Hive函式重要應用案例

Hive函式重要應用案例

第一章、多位元組分隔符

1.1 應用場景

Hive中的分隔符

Hive中預設使用單位元組分隔符來載入文字資料,例如逗號、製表符、空格等等,預設的分隔符為\001。根據不同檔案的不同分隔符,我們可以通過在建立表時使用 row format delimited fields terminated by ‘單位元組分隔符’ 來指定檔案中的分割符,確保正確將表中的每一列與檔案中的每一列實現一一對應的關係。

特殊資料

在實際工作中,我們遇到的資料往往不是非常規範化的資料,例如我們會遇到以下的兩種情況

情況一:每一行資料的分隔符是多位元組分隔符,例如:”||”、“--”等

上圖中每列的分隔符為||,為多位元組分隔符

情況二:資料的欄位中包含了分隔符

上圖中每列的分隔符為空格,但是資料中包含了分割符,時間欄位中也有空格

192.168.88.134 [08/Nov/2020:10:44:32 +0800] "GET / HTTP/1.1" 404 951

1.2 問題與需求

問題

基於上述的兩種特殊資料,我們如果使用正常的載入資料的方式將資料載入到表中,就會出以下兩種錯誤:

情況一:載入資料的分隔符為多位元組分隔符

  • 建立表
--如果表已存在就刪除表
drop table if exists singer;
--建立表
create table singer(
 id string,--歌手id
 name string,--
歌手名稱 country string,--國家 province string,--省份 gender string,--性別 works string--作品 ) --指定列的分隔符為|| row format delimited fields terminated by '||';
  • 載入資料
load data local inpath '/export/data/test01.txt' into table singer;
  • 檢視結果
select * from singer;
  • 問題

資料發生了錯位,沒有正確的載入每一列的資料

  • 原因

Hive中預設只支援單位元組分隔符,無法識別多位元組分隔符

情況二:資料中包含了分隔符


建立表

--如果表存在,就刪除表
drop table if exists apachelog;
--建立表
create table apachelog(
 ip string,      --IP地址
 stime string,    --時間
 mothed string,  --請求方式
 url string,     --請求地址
 policy string,  --請求協議
 stat string,    --請求狀態
 body string     --位元組大小
)
--指定列的分隔符為空格
row format delimited fields terminated by ' ';
  • 載入資料
load data local inpath '/export/data/apache_web_access.log' into table apachelog;
  • 檢視結果
select * from apachelog;
  • 問題

時間欄位被切分成了兩個欄位,後面所有的欄位出現了錯位

  • 原因

時間資料中包含了分隔符,導致Hive認為這是兩個欄位,但實際業務需求中,為一個欄位

需求

基於上面兩種情況的測試發現,當資料中出現了多位元組分隔符或者資料中的某個欄位包含了分隔符,就會導致資料載入錯位的問題。基於出現的問題,我們需要通過特殊的方法來解決該問題,即使當資料中出現多位元組分隔符等情況時,Hive也能正確的載入資料,實現列與資料的一一對應。

1.3 解決方案一:替換分隔符

方案概述

面對情況一,如果資料中的分隔符是多位元組分隔符,可以使用程式提前將資料中的多位元組分隔符替換為單位元組分隔符,然後使用Hive載入,就可以實現正確載入對應的資料。
例如:原始資料中的分隔符為“||”

程式開發

可以在ETL階段通過一個MapReduce程式,將“||”替換為單位元組的分隔符“|”,示例程式如下:

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;

import java.io.IOException;

/**
 * @ClassName ChangeSplitCharMR
 * @Description TODO MapReduce實現將多位元組分隔符轉換為單位元組符
 * @Create By  itcast
 */
public class ChangeSplitCharMR extends Configured implements Tool {
    public int run(String[] arg) throws Exception {
        /**
         * 構建Job
         */
        Job job = Job.getInstance(this.getConf(),"changeSplit");
        job.setJarByClass(ChangeSplitCharMR.class);

        /**
         * 配置Job
         */
        //input:讀取需要轉換的檔案
        job.setInputFormatClass(TextInputFormat.class);
        Path inputPath = new Path("datas/split/test01.txt");
        FileInputFormat.setInputPaths(job,inputPath);

        //map:呼叫Mapper
        job.setMapperClass(ChangeSplitMapper.class);
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(NullWritable.class);

        //reduce:不需要Reduce過程
        job.setNumReduceTasks(0);

        //output
        job.setOutputFormatClass(TextOutputFormat.class);
        Path outputPath = new Path("datas/output/changeSplit");
        TextOutputFormat.setOutputPath(job,outputPath);

        /**
         * 提交Job
         */
        return job.waitForCompletion(true) ? 0 : -1;
    }

    //程式入口
    public static void main(String[] args) throws Exception {
        //呼叫run
        Configuration conf = new Configuration();
        int status = ToolRunner.run(conf, new ChangeSplitCharMR(), args);
        System.exit(status);
    }

    public static class ChangeSplitMapper extends Mapper<LongWritable,Text,Text,NullWritable>{
        //定義輸出的Key
        private Text outputKey = new Text();
        //定義輸出的Value
        private NullWritable outputValue = NullWritable.get();

        @Override
        protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
            //獲取每條資料
            String line = value.toString();
            //將裡面的||轉換為|
            String newLine = line.replaceAll("\\|\\|", "|");
            //替換後的內容作為Key
            this.outputKey.set(newLine);
            //輸出結果
            context.write(this.outputKey,this.outputValue);
        }
    }
}

這樣做是直接解決資料的問題,雙豎線變單豎線,而沒有解決單位元組分隔符的問題。

程式執行結果如下:

重新建表載入資料

  • 重新建立Hive表
--如果表已存在就刪除表
drop table if exists singer;
--建立表
create table singer(
 id string,--歌手id
 name string,--歌手名稱
 country string,--國家
 province string,--省份
 gender string,--性別
 works string--作品
)
--指定列的分隔符為||
row format delimited fields terminated by '|';
  • 在Hive中重新載入資料
load data local inpath '/export/data/part-m-00000' into table singer;

檢視結果

總結

在ETL階段可以直接對資料進行分隔符的替換,通過替換分隔符將多位元組分隔符更改為單位元組分隔符,就可以解決資料載入的問題,但是這種方式有對應的優缺點,並不是所有的場景適用於該方法。

  • 優點:實現方式較為簡單,基於字串替換即可
  • 缺點:無法滿足情況2的需求

1.4 解決方案二:RegexSerDe正則載入

方案概述

面對情況一和情況二的問題,Hive中提供了一種特殊的方式來解決,Hive提供了一種特殊的Serde來載入特殊資料的問題,使用正則匹配來載入資料,匹配每一列的資料。

官網地址:https://cwiki.apache.org/confluence/display/Hive/GettingStarted#GettingStarted-ApacheWeblogData

什麼是SerDe?

Hive的SerDe提供了序列化和反序列化兩個功能,SerDe是英文Serialize和Deserilize的組合縮寫,用於實現將Hive中的物件進行序列化和將資料進行反序列化。

Serialize就是序列化,用於將Hive中使用的java object轉換成能寫入hdfs的位元組序列,或者其他系統能識別的流檔案。Hive中的insert語句用於將資料寫入HDFS,所以就會呼叫序列化實現。Hive中的呼叫過程如下:

Deserilize就是反序列化,用於將字串或者二進位制資料流轉換成Hive能識別的java object物件。所有Hive中的Select語句在查詢資料時,需要將HDFS中的資料解析為Hive中物件,就需要進行反序列化。Hive可以方便的將資料載入到表中而不需要對資料進行轉換,這樣在處理海量資料時可以節省大量的時間。Hive中的呼叫過程如下:

Hive中包含的SerDe

官網地址:https://cwiki.apache.org/confluence/display/Hive/SerDe

Hive中預設提供了多種SerDe用於解析和載入不同型別的資料檔案,常用的有ORCSerde 、RegexSerde、JsonSerDe等。

RegexSerDe的功能

RegexSerde是Hive中專門為了滿足複雜資料場景所提供的正則載入和解析資料的介面,使用RegexSerde可以指定正則表示式載入資料,根據正則表示式匹配每一列資料。上述過程中遇到的情況一和情況二的問題,都可以通過RegexSerDe使用正則表示式來載入實現。

RegexSerDe解決多位元組分隔符

分析資料格式,構建正則表示式

原始資料格式

01||周杰倫||中國||臺灣||男||七里香



正則表示式定義每一列

([0-9]*)\\|\\|(.*)\\|\\|(.*)\\|\\|(.*)\\|\\|(.*)\\|\\|(.*)


正則校驗



 基於正則表示式,使用RegexSerde建表
--如果表已存在就刪除表
drop table if exists singer;
--建立表
create table singer(
id string,--歌手id
name string,--歌手名稱
country string,--國家
province string,--省份
gender string,--性別
works string--作品
)
--指定使用RegexSerde載入資料
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.RegexSerDe'
--指定正則表示式
WITH SERDEPROPERTIES (
"input.regex" = "([0-9]*)\\|\\|([^}]*)\\|\\|([^}]*)\\|\\|([^}]*)\\|\\|([^}]*)\\|\\|([^}]*)"
);

 載入資料
load data local inpath '/export/data/test01.txt' into table singer;

 檢視資料結果
select * from singer;


每一列的資料都被正常的載入,沒有錯位

2.4.6 RegexSerDe解決資料中包含分割符
 分析資料格式,構建正則表示式
 原始資料格式
192.168.88.100 [08/Nov/2020:10:44:33 +0800] "GET /hpsk_sdk/index.html HTTP/1.1" 200 328

 正則表示式定義每一列
([^ ]*) ([^}]*) ([^ ]*) ([^ ]*) ([^ ]*) ([0-9]*) ([^ ]*)

 正則校驗


 基於正則表示式,使用RegexSerde建表
--如果表存在,就刪除表
drop table if exists apachelog;
--建立表
create table apachelog(
ip string, --IP地址
stime string, --時間
mothed string, --請求方式
url string, --請求地址
policy string, --請求協議
stat string, --請求狀態
body string --位元組大小
)
--指定使用RegexSerde載入資料
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.RegexSerDe'
--指定正則表示式
WITH SERDEPROPERTIES (
"input.regex" = "([^ ]*) ([^}]*) ([^ ]*) ([^ ]*) ([^ ]*) ([0-9]*) ([^ ]*)"
);

 載入資料
load data local inpath '/export/data/apache_web_access.log' into table apachelog;
 檢視資料結果
select ip,stime,url,stat,body from apachelog;


作者:王陸 出處:https://www.cnblogs.com/wkfvawl/

-------------------------------------------

個性簽名:罔談彼短,靡持己長。做一個謙遜愛學的人!

本站使用「署名 4.0 國際」創作共享協議,轉載請在文章明顯位置註明作者及出處。鑑於博主處於考研複習期間,有什麼問題請在評論區中提出,博主儘可能當天回覆,加微信好友請註明原因