Hive中的數據分桶以及使用場景


hive官網關於桶表的介紹:https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL+BucketedTables

hive官網關於tablesample的介紹:https://cwiki.apache.org/confluence/display/Hive/LanguageManual+Sampling

hive bucketing in apache spark:https://myslide.cn/slides/12290


 

hive中有數據分區的方案,也有數據分桶的方案,今天我們就來探討下數據分桶 以及數據分桶使用的場景。

該篇文章主要分為一下幾個部分:

1.數據分桶的適用場景

2.數據分桶的原理

3.數據分桶的作用

4.如何創建數據分桶表

5.如何將數據插入分桶表

6.針對於分桶表的數據抽樣

7.數據分桶的一些缺陷

 

數據分桶的適用場景:

    分區提供了一個隔離數據和優化查詢的便利方式,不過並非所有的數據都可形成合理的分區,

尤其是需要確定合適大小的分區划分方式,(不合理的數據分區划分方式可能導致有的分區數據過多,而某些分區沒有什么數據的尷尬情況)

試試分桶是將數據集分解為更容易管理的若干部分的另一種技術。

 

數據分桶的原理:

        

    跟MR中的HashPartitioner的原理一模一樣

        MR中:按照key的hash值去模除以reductTask的個數

        Hive中:按照分桶字段的hash值去模除以分桶的個數

    Hive也是 針對某一列進行桶的組織。Hive采用對列值哈希,然后除以桶的個數求余的方式決定該條記錄存放在哪個桶當中。

 

 

數據分桶的作用:

好處:
1、方便抽樣

2、提高join查詢效率

 

(1)獲得更高的查詢處理效率。桶為表加上了額外的結構,Hive 在處理有些查詢時能利用這個結構。具體而言,連接兩個在(包含連接列的)相同列上划分了桶的表,可以使用 Map 端連接 (Map-side join)高效的實現。比如JOIN操作。對於JOIN操作兩個表有一個相同的列,如果對這兩個表都進行了桶操作。那么將保存相同列值的桶進行JOIN操作就可以,可以大大較少JOIN的數據量。

(2)使取樣(sampling)更高效。在處理大規模數據集時,在開發和修改查詢的階段,如果能在數據集的一小部分數據上試運行查詢,會帶來很多方便。

 

創建數據分桶表:

    創建數據分桶表與普通表的表區別並不太大,如下為一個創建數據分桶表的示例:

use clickcube;
 
CREATE EXTERNAL TABLE `clickcube_mid`(             
   `logtype` bigint,                                
   `date` string,                                   
   `hour` bigint,                                   
   `projectid` bigint,                              
   `campaignid` bigint,                             
   `templateid` bigint,                             
   `mediaid` bigint,                                
   `slotid` bigint,                                 
   `channeltype` bigint,                            
   `regioncode` string,                             
   `campclick` bigint,                              
   `campimp` bigint,                                
   `mediaclick` bigint,                             
   `mediaimp` bigint,                               
   `templateimp` bigint,                            
   `templatecampimp` bigint,                        
   `mediaclickcost` double,                         
   `campclickcost` double)                          
  PARTITIONED BY (                                   
   `day` string) 
  CLUSTERED BY (
   `campaignid`,  `mediaid` ) INTO 100 BUCKETS                                   
  ROW FORMAT SERDE                                   
    'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe'  
  STORED AS INPUTFORMAT                              
    'org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat'  
  OUTPUTFORMAT                                       
    'org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat' 
  TBLPROPERTIES (                                    
    'last_modified_by'='cloudera-scm',               
    'last_modified_time'='1530676367',               
    'transient_lastDdlTime'='1530676367') 

其實主要注意的地方就如下的點:

  CLUSTERED BY (

   `campaignid`,  `mediaid` ) INTO 100 BUCKETS 

 

 

如何將數據插入分桶表

將數據導入分桶表主要通過以下步驟

第一步:

    從hdfs或本地磁盤中load數據,導入中間表

第二步:

    通過從中間表查詢的方式的完成數據導入

 

      分桶的實質就是對 分桶的字段做了hash 然后存放到對應文件中,所以說如果原有數據沒有按key hash ,

需要在插入分桶的時候hash, 也就是說向分桶表中插入數據的時候必然要執行一次MAPREDUCE,

這也就是分桶表的數據基本只能通過從結果集查詢插入的方式進行導入

 

 

這里我們主要講解第二步:

主要的過程我們寫為一個SQL

use clickcube;
 
set hive.enforce.bucketing = true;
 
INSERT OVERWRITE TABLE clickcube_mid_bucket 
PARTITION( day = '2018-07-03' )
SELECT 
    clickcube_mid.logtype,
    clickcube_mid.`date`,
    clickcube_mid.`hour`,
    clickcube_mid.projectid,
    clickcube_mid.campaignid,
    clickcube_mid.templateid,
    clickcube_mid.mediaid,
    clickcube_mid.slotid,
    clickcube_mid.channeltype,
    clickcube_mid.regioncode,
    clickcube_mid.campclick,
    clickcube_mid.campimp,
    clickcube_mid.mediaclick,
    clickcube_mid.mediaimp,
    clickcube_mid.templateimp,
    clickcube_mid.templatecampimp,
    clickcube_mid.mediaclickcost,
    clickcube_mid.campclickcost 
FROM clickcube_mid
WHERE day = '2018-07-03'

這里我們需要注意幾點

我們需要確保reduce 的數量與表中的bucket 數量一致,為此有兩種做法

 

1.讓hive強制分桶,自動按照分桶表的bucket 進行分桶。(推薦)

set  hive.enforce.bucketing = true;

 

2.手動指定reduce數量

set mapreduce.job.reduces = num;

/

set mapreduce.reduce.tasks = num;

並在 SELECT 后增加CLUSTER BY 語句

 

下面展示下整體的數據導入腳本

主要分為3個文件:

-rw-r--r--. 1 root root  637 7月   4 20:37 insert_into_bucket.hql
-rw-r--r--. 1 root root   37 7月   4 20:26 insert_into_bucket.init
-rwxr-xr-x. 1 root root 1788 7月   4 20:27 insert_into_bucket.sh

insert_into_bucket.hql   數據導入HQL

insert_into_bucket.init   設置初始環境

insert_into_bucket.sh     主體執行腳本

 

insert_into_bucket.sh 

#! /bin/bash
 
set -o errexit
 
source /etc/profile
source ~/.bashrc
 
ROOT_PATH=$(dirname $(readlink -f $0))
echo $ROOT_PATH
 
date_pattern_old='^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$'
date_pattern='^[0-9]{4}-((0([1-9]{1}))|(1[1|2]))-(([0-2]([0-9]{1}))|(3[0|1]))$'
 
#參數數量
argsnum=$#
 
#一些默認值
curDate=`date +%Y%m%d`
partitionDate=`date -d '-1 day' +%Y-%m-%d`
fileLocDate=`date -d '-1 day' +%Y-%m-%d`
 
#日志存放位置
logdir=insert_bucket_logs
 
function tips() { 
    echo "Usage : insert_into_bucket.sh [date]"
    echo "Args :"
    echo "date"
    echo "    date use this format yyyy-MM-dd , ex : 2018-06-02"
        echo "============================================================"
    echo "Example :"
    echo "    example1 : sh insert_into_bucket.sh"
    echo "    example2 : sh insert_into_bucket.sh 2018-06-02"
}
 
if [ $argsnum -eq 0 ] ; then
    echo "No argument, use default value"
elif [ $argsnum -eq 1 ] ; then
    echo "One argument, check date pattern"
    arg1=$1
    if ! [[ "$arg1" =~ $date_pattern ]] ; then
               echo -e "\033[31m Please specify valid date in format like 2018-06-02"
               echo -e "\033[0m"
               tips
            exit 1
    fi
    dateArr=($(echo $arg1 |tr "-" " "))
    echo "dateArr length is "${#dateArr[@]}
    partitionDate=${dateArr[0]}-${dateArr[1]}-${dateArr[2]}
else 
    echo -e "\033[31m Not valid num of arguments"
    echo -e "\033[0m"
    tips
    exit 1
fi
 
 
if [ ! -d "$logdir" ]; then
    mkdir -p $logdir
fi
 
 
cd $ROOT_PATH
 
#nohup hive -hivevar p_date=${partitionDate} -hivevar f_date=${fileLocDate} -f  hdfs_add_partition_dmp_clearlog.hql  >> $logdir/load_${curDate}.log
 
nohup beeline -u jdbc:hive2://master:10000 -n root --color=true --silent=false  --hivevar p_date=${partitionDate} -i insert_into_bucket.init -f insert_into_bucket.hql  >> $logdir/insert_bucket_${curDate}.log

insert_into_bucket.init 

set hive.enforce.bucketing = true;

insert_into_bucket.hql

use clickcube;
 
INSERT OVERWRITE TABLE clickcube_mid_bucket 
PARTITION( day = '${hivevar:p_date}' )
SELECT 
    clickcube_mid.logtype,
    clickcube_mid.`date`,
    clickcube_mid.`hour`,
    clickcube_mid.projectid,
    clickcube_mid.campaignid,
    clickcube_mid.templateid,
    clickcube_mid.mediaid,
    clickcube_mid.slotid,
    clickcube_mid.channeltype,
    clickcube_mid.regioncode,
    clickcube_mid.campclick,
    clickcube_mid.campimp,
    clickcube_mid.mediaclick,
    clickcube_mid.mediaimp,
    clickcube_mid.templateimp,
    clickcube_mid.templatecampimp,
    clickcube_mid.mediaclickcost,
    clickcube_mid.campclickcost  
FROM clickcube_mid
WHERE day = '${hivevar:p_date}'

針對於分桶表的數據抽樣:

分桶的一個主要優勢就是數據抽樣,

主要有兩種方式

1)基於桶抽樣

2)基於百分比抽樣

 

1)基於桶抽樣:

hive> SELECT * FROMbucketed_users 
>   TABLESAMPLE(BUCKET 1 OUT OF 4 ON id); 
0 Nat 
4 Ann 

桶的個數從1開始計數。因此,前面的查詢從4個桶的第一個中獲取所有的用戶。 對於一個大規模的、均勻分布的數據集,這會返回表中約四分之一的數據行。我們 也可以用其他比例對若干個桶進行取樣(因為取樣並不是一個精確的操作,因此這個 比例不一定要是桶數的整數倍)。

 

說法一:

注:tablesample是抽樣語句,語法:TABLESAMPLE(BUCKET x OUTOF y)

    y必須是table總bucket數的倍數或者因子。hive根據y的大小,決定抽樣的比例。例如,table總共分了64份,當y=32時,抽取(64/32=)2個bucket的數據,當y=128時,抽取(64/128=)1/2個bucket的數據。

    x表示從哪個bucket開始抽取。例如,table總bucket數為32,tablesample(bucket 3 out of 16),表示總共抽取(32/16=)2個bucket的數據,分別為第3個bucket和第(3+16=)19個bucket的數據。

 

說法二:

   分桶語句中的分母表示的是數據將會被散列的桶的個數,

分子表示將會選擇的桶的個數。

 

示例:

 SELECT COUNT(1) 

FROM clickcube_mid_bucket 

TABLESAMPLE(BUCKET 10 OUT OF 100 ON rand()) 

WHERE day='2018-07-03';

 

 

2)基於百分比抽樣:

    hive另外一種按照抽樣百分比進行抽樣的方式,該種方式基於行數,按照輸入路徑下的數據塊的百分比進行抽樣。

    這種抽樣的最小單元是一個hdfs數據塊,如果表的數據大小小於普通塊大小128M,將返回所有行。

 

基於百分比的抽樣方式提供了一個變量,用於控制基於數據塊的調優種子信息:

<property>

    <name>hive.sample.seednumber</name>

    <value>0</value>

</property>

A number userd for percentage sampling. By changing this number, user will change the subsets of data sampled.

 

數據分桶存在的一些缺陷:

    如果通過數據文件LOAD 到分桶表中,會存在額外的MR負擔。

    實際生產中分桶策略使用頻率較低,更常見的還是使用數據分區。

 

轉載自https://blog.csdn.net/u010003835/article/details/80911215


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM