Hive编程指南 第9章 模式设计

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/myvanguard/article/details/86670421

Hive是反模式的。本章将重点介绍Hive 中哪些模式是用户应该使用的,而又有哪些反模式是应该避免使用的。

9.1按天划分的表

按天划分表就是一种模式,其通常会在表名中加入一个时间戳,例如表名为
upply_2011_01_01、supply_2011_01_02,等等。在Hive中,这种情况下应该使用分区表。

CREATE TABIE supply (id int, part string, quantity int)
PARTITIONED BY (int day); 

ALTER TABLE supply add PARTITION (day=20110102); 
ALTER TABLE supply add PARTITION(day=20110103); 
ALTER TABLE supply add PARTITION (day=20110102);

SELECT part, quantity FROM supply 
WHERE day>=20110102 AND day<20110103 AND quantity<4;

9.2关于分区

Hive中分区的功能是非常有用的。通过创建很多的分区确实可以优化一些查询,但是同时可能会对其他一些重要的查询不利,使用过多分区可能导致的一个问题就是会创建大量的非必须的Hadoop文件和文件夹。

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

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

按时间范围进行分区的一个好的策略就是按照不同的时间粒度来确定合适大小的数据积累量,而且安装这个时间粒度。随着时间的推移,分区数量的增长是“均匀的”.

CREATE TABLE weblogs(url string,time long,state string,city string)
PARTITIONED BY(day int);
SELECT * FROM weblogs WHERE day=20110102;

另一个解决方案是使用两个级别的分区并且使用不同的维度。
例如,第一个分区可能是按照天(day)进行划分的,而二级分区可能通过如州名(state)这样的地理区域进行划分:

CREATE TABLE weblogs(url string,time 1ong,city string)
PARTITIONED BY(day int,state string);
SELECT * FROM weblogs WHERE day=20110102

如果用户不能够找到好的、大小相对合适的分区方式的话,那么可以考虑使用分桶存储

9.3唯一键和标准化

关系型数据库通常使用唯一键、索引和标准化来存储数据集,通常是全部或者大部分存储到内存的。然而,Hive没有主键或基于序列密钥生成的自增键的概念。复杂的数据类型,如array、map和struct,有助于实现在单行中存储一对多数据。

避免标准化的主要的原因是为了最小化磁盘寻道,比如那些通常需要外键关系的情况。

CREATE TABLE employees(
name String,
salary FLOAT,
subordinates ARRAY<STRING>,
deductions MAP<STRING,FLOAT>,
address STRUCT<street:STRING,city:STRING,state:STRING,zip:INT>
);

9.4同一份数据多种处理

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

FROM history 
INSERT OVERWRITE sales SELECT * WHERE action='purchased'
INSERT OVERWRITE credits SELECT * WHERE action='returned';

9.5对于每个表的分区

很多的ETL处理过程会涉及到多个处理步骤,而每个步骤可能会产生一个或多个临时表,这些表仅供下一个job使用。起先可能会觉得将这些临时表进行分区不是那么有必要的。

设计了一个名为distinct_ip_in_logs的中间表

INSERT OVERWRITE table distinct ip in logs SELECT distinct(ip) as ip 
from weblogs WHERE hit date='2011-11-01'; 
CREATE TABLE state_city_for_day (state string, city string); 
INSERT OVERWRITE state city for day SELECT distinct(state, city) 
FROM distinct ip in logs JOIN geodata 
ON (distinct_ip_in_logs. ip=geodata. ip);

一个更具鲁棒性的处理方法是在整个过程中使用分区。

INSERT OVERWRITE table distinct_ip_in_logs 
PARTITION(hit_date='2011-01-01')
SELECT distinct(ip) as ip from weblogs 
WHERE hit_date='2011-01-01'; 
CREATE TABLE state_city_for_day (state string, city string)
PARTITIONED BY (hit_date string); 
INSERT OVERWRITE table state_city for day PARTITION(hit_date)
SELECT distinct(state, city) FROM distinct ip in logs JOIN geodata 
ON(distinct_ip_in_logs_ip=geodata.ip)
WHERE(hit_date='2011-01-01');

9.6分桶表数据存储

分区提供一个隔离数据和优化查询的便利的方式。分桶是将数据集分解成更容易管理的若干部分的另一个技术。

不过,如果我们对表weblog进行分桶,并使用user_id字段作为分桶字段,则字段值会根据用户指定的值进行哈希分发到桶中。同一个user_id下的记录通常会存储到同一个桶内。假设用户数要比桶数多得多,那么每个桶内就将会包含多个用户的记录:

CREATE TABLE weblog(user id INT,url STRING,source ip STRING)
PARTITIONED BY(dt STRING)
CLUSTERED BY(user id)INTO 96 BUCKETS;

分桶有几个优点。因为桶的数量是固定的,所以它没有数据波动。
正确地填充表

set mapred.reduce.tasks=96
SET hive.enforce.bucketing=true; 
FROM raw logs 
INSERT OVERWRITE TABLE weblog PARTITION (dt='2009-02-25')
SELECT user_id, url, source ip WHERE dt=12009-02-25';

9.7为表增加列

Hive 允许在原始数据文件之上定义一个模式,不要求必须以特定的格式转换和导入数据。这样的分离方式的好处是,当为数据文件增加新的字段时,可以容易地适应表定义的模式。

Hive 提供了SerDe抽象,其用于从输入中提取数据。
一个SerDe通常是从左到右进行解析的。通过指定的分隔符将行分解成列。
如果某行的字段个数比预期的要少,那么缺少的字段将返回null
如果某行的字段个数比预期的要多,那么多出的字段将会被省略掉

CREATE TABLE weblogs (version LONG,url STRING)
PARTITIONED BY (hit date int)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t';

9.8使用列存储表

Hive 通常使用行式存储,不过Hive也提供了一个列式SerDe来以混合列式格式存储信息。

9.8.1重复数据

假设有足够多的行,列有很多重复的数据。这种类型的数据如果使用列式存储将会非常好

9.8.2多列表

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

SELECT distinct(state)from weblogs;

9.9(几乎)总是使用压缩

几乎在所有情况下,压缩都可以使磁盘上存储的数据量变小,这样可以通过降低I/O来提高查询执行速度。但是压缩和解压缩都会消耗CPU资源。

猜你喜欢

转载自blog.csdn.net/myvanguard/article/details/86670421