Hive看上去很像关系型数据库。不过,Hive实现和使用的方式和传统的关系型数据库非常不同。Hive是反模式的。

本文将重点介绍Hive中哪些模式是用户应该使用的,儿哪些是应该避免的

一、按天划分的表

这种每天一张表的方式在数据库领域是反模式的一种方式,但因为实际情况下,数据集增长的很快,这种方式应用还是比较广泛的。

对于Hive,这种情况下应该使用分区表。

Hive通过where子句中的表达式来选择查询所需的指定的分区。这样的产需效率高,而且看起来清晰明了。

二、关于分区

Hive中分区的功能非常有用的。这是因为Hive通常要对输入进行全盘扫描,来满足查询条件。通过创建很多分区确实可以优化一些铲鲟,但是同时可能会对其他一些重要的查询不利:

HDFS用于设计存储数百万的大文件,而非数十亿的小文件。使用过多分区可能导致的一个问题就是创建大量的非必须的Hadoop文件和文件夹。一个分区就对应着一个包含多个文件的文件夹。如果指定的表存在数百个分区吗,那么可能每天都会创建好几万个文件。如果保持这样的表很多年,那么最终就会超出NameNode对系统云数据信息的处理能力。因为NameNode必须要将所有的系统文件的原信息保存在内存中。

虽然每个文件只需要少量字节大小的元数据(大约是150字节/文件),但是这样也会限制一个HDFS实例所能管理的文件总数的上限。而其他文件系统,比如MapR和Amazon S3就没有这个限制。

MapReduce会将一个任务(job)转换成多个任务(task)。默认情况下,每个task都是一个新的JVM实例,都需要开启和销毁的开销。对于小文件,每个文件都会对应一个task。在一些情况下,JVM开启和销毁的时间中销毁可能会比实际处理数据的时间消耗要长。

因此。一个理想的分区方案不应该导致产生太多的分区文件和目录,并且每个目录下的文件应该足够的大,应该是文件系统中块大小的若干倍。

1、单一按照时间分区

create table weblogs(url string,time long,state string,city string)

partition by (day int);

select * from weblogs where day=20191113;

2、多条件复合分区

create table weblogs(url string,time long,city string)

partition by (day int,state string);

select * from weblogs where day=20191112;

三、唯一键和标准化

关系型数据库通常使用唯一键、索引和标准化来存储数据集,通常是全部或者大部分存储到内存的。然而,Hive没有主键或基于序列密钥生成的自增键的概念。如果可以的话,应避免对非标准化数据进行连接(join)操作。复杂的数据类型,如array、map、struct,有助于实现在单行中存储一对多数据。这并不是说不应该进行标准化,但是星星架构类型设计并非最优秀。

避免标准化的主要原因是为了最小化磁盘寻道,比如那些通常需要外键关系的情况。非标准化数据允许被扫描或写入到大的、连续的磁盘存储区域,从而优化磁盘驱动器的I/O性能。然而,非标准化可能导致数据重复,而且有更大的导致数据不一致的风险。

例子:对员工表做非结构化调整:

create table employees(

name string,

salary float,

subordinates array<string>,

deductions MAP<string,float>,

address struct<street:strng,city:string,state:string,zip:int>

);

四、同一份数据多种处理

Hive本身提供了一个独特的语法,它可以从一个数据源产生多个数据聚合,而无需每次聚合都要重新扫描一次。对于大的数据输入集来说,这个优化可以节约非常可观的时间。

例子:下面2个查询都会从history表读取数据,然后导入2个不同的表中:

insert overwrite table sales

select * from history where action='purchased',

insert overwrite table credits

select * from history where action='returned';

上面可以改为,只需扫描history表一次即可

from history

insert overwrite sales select * where action='purchased'

insert overwrite credits select * where action='returned';

五、对于每个分区表

六、分桶表数据存储

本文链接:https://blog.csdn.net/u010003835/article/details/80911215

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)更高效。在处理大规模数据集时,在开发和修改查询的阶段,如果能在数据集的一小部分数据上试运行查询,会带来很多方便。

创建数据分桶表:

创建数据分桶表与普通表的表区别并不太大,如下为一个创建数据分桶表的示例:

  1.  
    use clickcube;
  2.  
     
  3.  
    CREATE EXTERNAL TABLE `clickcube_mid`(
  4.  
    `logtype` bigint,
  5.  
    `date` string,
  6.  
    `hour` bigint,
  7.  
    `projectid` bigint,
  8.  
    `campaignid` bigint,
  9.  
    `templateid` bigint,
  10.  
    `mediaid` bigint,
  11.  
    `slotid` bigint,
  12.  
    `channeltype` bigint,
  13.  
    `regioncode` string,
  14.  
    `campclick` bigint,
  15.  
    `campimp` bigint,
  16.  
    `mediaclick` bigint,
  17.  
    `mediaimp` bigint,
  18.  
    `templateimp` bigint,
  19.  
    `templatecampimp` bigint,
  20.  
    `mediaclickcost` double,
  21.  
    `campclickcost` double)
  22.  
    PARTITIONED BY (
  23.  
    `day` string)
  24.  
    CLUSTERED BY (
  25.  
    `campaignid`, `mediaid` ) INTO 100 BUCKETS
  26.  
    ROW FORMAT SERDE
  27.  
    'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe'
  28.  
    STORED AS INPUTFORMAT
  29.  
    'org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat'
  30.  
    OUTPUTFORMAT
  31.  
    'org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat'
  32.  
    TBLPROPERTIES (
  33.  
    'last_modified_by'='cloudera-scm',
  34.  
    'last_modified_time'='1530676367',
  35.  
    'transient_lastDdlTime'='1530676367')

其实主要注意的地方就如下的点:

CLUSTERED BY (

`campaignid`,  `mediaid` ) INTO 100 BUCKETS

如何将数据插入分桶表

将数据导入分桶表主要通过以下步骤

第一步:

从hdfs或本地磁盘中load数据,导入中间表

第二步:

通过从中间表查询的方式的完成数据导入

分桶的实质就是对 分桶的字段做了hash 然后存放到对应文件中,所以说如果原有数据没有按key hash ,

需要在插入分桶的时候hash, 也就是说向分桶表中插入数据的时候必然要执行一次MAPREDUCE,

这也就是分桶表的数据基本只能通过从结果集查询插入的方式进行导入

这里我们主要讲解第二步:

主要的过程我们写为一个SQL

  1.  
    use clickcube;
  2.  
     
  3.  
    set hive.enforce.bucketing = true;
  4.  
     
  5.  
    INSERT OVERWRITE TABLE clickcube_mid_bucket
  6.  
    PARTITION( day = '2018-07-03' )
  7.  
    SELECT
  8.  
    clickcube_mid.logtype,
  9.  
    clickcube_mid.`date`,
  10.  
    clickcube_mid.`hour`,
  11.  
    clickcube_mid.projectid,
  12.  
    clickcube_mid.campaignid,
  13.  
    clickcube_mid.templateid,
  14.  
    clickcube_mid.mediaid,
  15.  
    clickcube_mid.slotid,
  16.  
    clickcube_mid.channeltype,
  17.  
    clickcube_mid.regioncode,
  18.  
    clickcube_mid.campclick,
  19.  
    clickcube_mid.campimp,
  20.  
    clickcube_mid.mediaclick,
  21.  
    clickcube_mid.mediaimp,
  22.  
    clickcube_mid.templateimp,
  23.  
    clickcube_mid.templatecampimp,
  24.  
    clickcube_mid.mediaclickcost,
  25.  
    clickcube_mid.campclickcost
  26.  
    FROM clickcube_mid
  27.  
    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

  1.  
    #! /bin/bash
  2.  
     
  3.  
    set -o errexit
  4.  
     
  5.  
    source /etc/profile
  6.  
    source ~/.bashrc
  7.  
     
  8.  
    ROOT_PATH=$(dirname $(readlink -f $0))
  9.  
    echo $ROOT_PATH
  10.  
     
  11.  
    date_pattern_old='^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$'
  12.  
    date_pattern='^[0-9]{4}-((0([1-9]{1}))|(1[1|2]))-(([0-2]([0-9]{1}))|(3[0|1]))$'
  13.  
     
  14.  
    #参数数量
  15.  
    argsnum=$#
  16.  
     
  17.  
    #一些默认值
  18.  
    curDate=`date +%Y%m%d`
  19.  
    partitionDate=`date -d '-1 day' +%Y-%m-%d`
  20.  
    fileLocDate=`date -d '-1 day' +%Y-%m-%d`
  21.  
     
  22.  
    #日志存放位置
  23.  
    logdir=insert_bucket_logs
  24.  
     
  25.  
    function tips() {
  26.  
    echo "Usage : insert_into_bucket.sh [date]"
  27.  
    echo "Args :"
  28.  
    echo "date"
  29.  
    echo " date use this format yyyy-MM-dd , ex : 2018-06-02"
  30.  
    echo "============================================================"
  31.  
    echo "Example :"
  32.  
    echo " example1 : sh insert_into_bucket.sh"
  33.  
    echo " example2 : sh insert_into_bucket.sh 2018-06-02"
  34.  
    }
  35.  
     
  36.  
    if [ $argsnum -eq 0 ] ; then
  37.  
    echo "No argument, use default value"
  38.  
    elif [ $argsnum -eq 1 ] ; then
  39.  
    echo "One argument, check date pattern"
  40.  
    arg1=$1
  41.  
    if ! [[ "$arg1" =~ $date_pattern ]] ; then
  42.  
    echo -e "\033[31m Please specify valid date in format like 2018-06-02"
  43.  
    echo -e "\033[0m"
  44.  
    tips
  45.  
    exit 1
  46.  
    fi
  47.  
    dateArr=($(echo $arg1 |tr "-" " "))
  48.  
    echo "dateArr length is "${#dateArr[@]}
  49.  
    partitionDate=${dateArr[0]}-${dateArr[1]}-${dateArr[2]}
  50.  
    else
  51.  
    echo -e "\033[31m Not valid num of arguments"
  52.  
    echo -e "\033[0m"
  53.  
    tips
  54.  
    exit 1
  55.  
    fi
  56.  
     
  57.  
     
  58.  
    if [ ! -d "$logdir" ]; then
  59.  
    mkdir -p $logdir
  60.  
    fi
  61.  
     
  62.  
     
  63.  
    cd $ROOT_PATH
  64.  
     
  65.  
    #nohup hive -hivevar p_date=${partitionDate} -hivevar f_date=${fileLocDate} -f hdfs_add_partition_dmp_clearlog.hql >> $logdir/load_${curDate}.log
  66.  
     
  67.  
    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
  68.  
     

insert_into_bucket.init

set hive.enforce.bucketing = true;
 

insert_into_bucket.hql

  1.  
    use clickcube;
  2.  
     
  3.  
    INSERT OVERWRITE TABLE clickcube_mid_bucket
  4.  
    PARTITION( day = '${hivevar:p_date}' )
  5.  
    SELECT
  6.  
    clickcube_mid.logtype,
  7.  
    clickcube_mid.`date`,
  8.  
    clickcube_mid.`hour`,
  9.  
    clickcube_mid.projectid,
  10.  
    clickcube_mid.campaignid,
  11.  
    clickcube_mid.templateid,
  12.  
    clickcube_mid.mediaid,
  13.  
    clickcube_mid.slotid,
  14.  
    clickcube_mid.channeltype,
  15.  
    clickcube_mid.regioncode,
  16.  
    clickcube_mid.campclick,
  17.  
    clickcube_mid.campimp,
  18.  
    clickcube_mid.mediaclick,
  19.  
    clickcube_mid.mediaimp,
  20.  
    clickcube_mid.templateimp,
  21.  
    clickcube_mid.templatecampimp,
  22.  
    clickcube_mid.mediaclickcost,
  23.  
    clickcube_mid.campclickcost
  24.  
    FROM clickcube_mid
  25.  
    WHERE day = '${hivevar:p_date}'
  26.  
     

针对于分桶表的数据抽样:

分桶的一个主要优势就是数据抽样,

主要有两种方式

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负担。

    

    实际生产中分桶策略使用频率较低,更常见的还是使用数据分区。

七、为表增加列

八、使用列存储

1、重复数据

2、多列

对于非常多列的字段,查询只会使用到一个字段或者很少的字段。基于列示存储将会使得分析表数据执行的更快。

九、总是使用压缩

 

 
 

Hive 模式设计的更多相关文章

  1. 分享基于Entity Framework的Repository模式设计(附源码)

    关于Repository模式,在这篇文章中有介绍,Entity Framework返回IEnumerable还是IQueryable? 这篇文章介绍的是使用Entity Framework实现的Rep ...

  2. php模式设计之 观察者模式

    这是我写的<php模式设计>的第五篇.前面的四篇在不断学习不断加深认识,到了今天再看观察者模式,觉得非常容易理解.这也许就是我们积少成多的结果吧.希望还是能够不断进步. 开篇还是从名字说起 ...

  3. php模式设计之 适配器模式

    在这个有没有对象都要高呼“面向对象”的年代,掌握面向对象会给我们带来意想不到的方便.学编程的小伙伴从开始能写几行代码实现简单功能到后来懂得将一些重复的操作组合起来形成一个“函数”,再到后来将“函数”和 ...

  4. php模式设计之 注册树模式

    在前两篇单例模式和工厂模式后,终于迎来了最后一个基础的设计模式--注册树模式. 什么是注册树模式? 注册树模式当然也叫注册模式,注册器模式.之所以我在这里矫情一下它的名称,是因为我感觉注册树这个名称更 ...

  5. php模式设计之 工厂模式

    承接上篇php模式设计之 单例模式,(虽然好像关系不大).今天讲述第二种基础的模式设计——工厂模式. 那么何为工厂模式? 从名字来看,似乎看不出什么端倪.工厂模式,和生产有关?还是和生产流程有关?难道 ...

  6. php模式设计之 单例模式

    模式设计是什么?初学者一开始会被这高大上的名称给唬住.而对于有丰富编程经验的老鸟来说,模式设计又是无处不在.很多接触的框架就是基于各种模式设计形成的. 简单说,在写代码的过程中一开始往往接触的是面向过 ...

  7. JavaScript高级---门面模式设计

    门面模式 两个作用: 1.简化类的接口 2.消除类与使用它的客户代码之间的耦合 门面模式常常是开发人员最亲密的朋友.它几乎是所有javascript库的核心原则 门面模式的目的是为了让开发人员用更简单 ...

  8. JavaScript高级---组合模式设计

    一.设计模式 javascript里面给我们提供了很多种设计模式: 工厂.桥.组合.门面.适配器.装饰者.享元.代理.观察者.命令.责任链 在前面我们实现了工厂模式和桥模式 工厂模式 : 核心:为了生 ...

  9. JavaScript高级---桥模式设计

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/stri ...

随机推荐

  1. python模块知识四 包和logging日志

    11.包 包:文件夹下具有__init__.py文件就是一个包,包用来管理多个模块 包的结构如下: bake ├── __init__.py ├── api ├── __init__.py ├── p ...

  2. 数组中重复的数字(Python)

    #!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2019-08-13 22:35 # @Author : daryl # @File : ...

  3. 【LEETCODE】49、数组分类,简单级别,题目:566,1089

    package y2019.Algorithm.array; /** * @ProjectName: cutter-point * @Package: y2019.Algorithm.array * ...

  4. 按键消抖——task任务和仿真平台搭建

    一.按键抖动原理 按键抖动原理:按键存在一个反作用弹簧,因此当按下或者松开时均会产生额外的物理抖动,物理抖动会产生电平的抖动. 消抖方法:一般情况下,抖动的总时间会持续20ms以内,按下按键后,等20 ...

  5. Dubbo快速入门 五

    5. Dubbo注解版 之前在dubbo配置文件显式编写内容提供者和消费者,官方还提供了了一种注解方式,接下来改造项目 1.服务提供方 dubbo配置文件 将之前手动申明注释掉,添加<dubbo ...

  6. Spring MVC传输对象属性

    今天搬砖时遇到一个问题,前端使用JSP+form传输数据,后台使用Spring MVC接收,但是接收到的对象属性一直是null,找了好久才发现原因,代码如下 前端代码   后端代码   需要注意一点 ...

  7. c# 获取网页的爬虫程序

    转载于:https://www.cnblogs.com/wzk153/p/9145684.html HtmlAgilityPack相关详解: https://www.cnblogs.com/asxin ...

  8. springboot笔记05——profile多环境配置切换

    前言 一个应用程序从开发到上线,往往需要经历几个阶段,例如开发.测试.上线.每个阶段所用到的环境的配置可能都是不一样的,Springboot 应用可以很方便地在各个环境中对配置进行切换.所以,今天主要 ...

  9. JAVA日期格式化yyyyMMddHHmmssSSS

    String nowtime = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());

  10. python day 11: 类的补充,元类,魔法方法,异常处理

    目录 python day 11 1. 类的补充 1.1 通过反射来查找类,创建对象,设置对象的属性与方法 1.2 类的魔法方法:getitem,setitem 1.3 元类__metaclass__ ...