大数据技术之Hive简介及底层原理

简介

一、概述

  1. Apache Hive™数据仓库软件有助于使用SQL读取,写入和管理驻留在分布式存储中的大型数据集。可以将结构投影到已经存储的数据上。提供了命令行工具和JDBC驱动程序以将用户连接到Hive。
  2. 提供了类SQL(HQL)语言来管理HDFS上的数据,底层会将sql转化为MapReduce执行,Hive适用于离线分析
  3. 在Hive中,每一个database在HDFS上对应一个目录
  4. 在Hive中没有主键的概念
  5. 一个表一旦建立之后,这个表中字段之间的间隔符就不能修改了

命令

  1. create table p2 like person; 建立一个p2表和person表一样结构
  2. insert overwrite local directory ‘/home/hivedemo’ row format delimited fields terminated by ’ ’ select * from person; 将查询数据传给本地目录中,以空格间隔,要求目录不存在,否则会被覆盖

表结构

内部表和外部表

概念
  1. 内部表:先在hive里建一张表,然后向这个表插入数据(用insert可以插入数据,也可以通过加载外部文件方式来插入数据),这样的表称之为hive的内部表
  2. 外部表:HDFS里已经有数据了,然后,通过hive创建一张表来管理这个文件数据。则这样表称之为外部表。需要注意的是,hive外部表管理的是HDFS里的某一个目录下的文件数据外部表创建命令
    进入hive,执行:create external table stu (id int,name string) row format delimited fields terminated by ’ ’ location
区别
  1. 对于内部表,在删除该表的时候,HDFS对应的目录节点会被删除 2. 对于外部表,在删除该表的时候,HDFS对应的目录节点不会删除

分区表

  1. 分区表的作用往往是对数据进行分类
  2. 基本操作

建表

create table cities(id int, name string) partitioned by(province string) row format delimited fields terminated by ’ ';

加载hebei分区数据

load data local inpath ‘/home/hivedata/hebei.txt’ into table cities partition(province = ‘hebei’);

加载guangdong分区数据

load data local inpath ‘/home/hivedata/guangdong.txt’ into table cities partition(province = ‘guangdong’);

加载jiangsu分区数据

load data local inpath ‘/home/hivedata/jiangsu.txt’ into table cities partition(province = ‘jiangsu’);
1.每一个分区对应一个目录
2.如果在HDFS上新建了目录作为分区,那么需要在Hive中来手动添加分区
alter table cities add partition(province=‘shandong’) location ‘/user/hive/warehouse/hivedemo.db/cities/province=shandong’;

修复表,但是注意这句SQL执行不稳定,有时候会执行失败

msck repair table cities;
1.在Hive的分区表中,如果根据分区字段进行查询,那么只查询分区对应的目录从而提高查询效率;如果跨分区查询,查询效率反而会降低
2.在分区表中,分区字段在原始数据中是不存在的,是需要在加载数据的时候手动指定的
3.如果分区字段在原始文件中已经存在,那么需要进行动态分区
1.先建立一张表管理原始数据
create table c_tmp(cid int, cname string, cpro string) row format delimited fields terminated by ’ ';
2. 加载数据
load data local inpath ‘/home/hivedata/cities.txt’ into table c_tmp;
3. 开启动态分区机制 - 从一张未分区表中查询数据放到一张已分区的表中,这个过程称之为动态分区
set hive.exec.dynamic.partition.mode=nonstrict;
1.指定分区字段进行动态分区
insert into table cities partition(province) select cid, cname, cpro from c_tmp distribute by cpro;

  1. 删除分区:alter table cities drop partition(province = ‘liaoning’);
  2. 在Hive中,支持多字段分区,前边的字段会包含后边的字段。指定了几个字段,就会形成一个几级的目录。当需要对数据进行多级分类的时候,那么这个时候可以多字段分区 - 实际开发中,往往是需要对数据进行多级分类

建表语句

create table product(id int, name string) partitioned by(kind string, sub string, subkind string) row format delimited fields terminated by ’ ';

加载数据

load data local inpath ‘/home/hivedata/shirt.txt’ into table product partition(kind = ‘clothes’, sub=‘coat’, subkind=‘Tshirt’);
load data local inpath ‘/home/hivedata/sports.txt’ into table product partition(kind = ‘clothes’, sub = ‘shoes’, subkind = ‘sports’);

分桶表

  1. 分桶表的作用是对数据进行抽样
  2. 在Hive中,分桶表默认是不开启的,所以需要手动开启分桶机制
  3. 案例

开启分桶机制

set hive.enforce.bucketing=true;

建立分桶表 - 表示根据name字段进行分桶 - 在分桶的时候会计算name字段的哈希码,然后利用哈希码进行二次计算确定放在哪个桶中

create table c_bucket(id int, name string) clustered by(name) into 6 buckets row format delimited fields terminated by ’ ';

向分桶表中添加数据

insert overwrite table c_bucket select id, name from cities;

抽样

select * from c_bucket tablesample (bucket 1 out of 3 on name);
1.bucket x out of y:x表示起始桶编号,从1开始计算,y表示的是步长。bucket 1 out of 3表示从第1个桶开始抽取数据,每隔3个桶抽取一次。其中x<=y

数据类型

  1. Hive分为基本类型和复杂类型
  2. 复杂类型
  3. array:数组类型,对应了Java中的数组或者集合

建表

create table arr(nums1 array<int>, nums2 array<int>) row format delimited fields terminated by ' ' collection items terminated by ',';

加载数据

load data local inpath ‘/home/hivedata/nums’ into table arr;

非空查询

select nums1[5] from arr where nums1[5] is not null;

  1. map:映射类型。对应了Java中的映射Map

建表

create table infos (id int, info map<string,int>) row format delimited fields terminated by ’ ’ map keys terminated by ‘,’;

加载数据

load data local inpath ‘/home/hivedata/map.txt’ into table infos;

查询tom对应的值

select info[‘tom’] from infos where info[‘tom’] is not null;

  1. struct:结构体类型。对应了Java中的对象

看作一个对象的四个属性来建表

create external table scores(s struct<name:string, chinese:int, math:int, english:int>) row format delimited collection items terminated by ’ ’ location ‘/score’;

查询每一个人的语文成绩

select s.chinese from scores;

explode

  1. explode会将数组中的每一个元素取出来作为单独的一行处理
  2. 案例:单词统计
  3. 方式一

建表管理HDFS存在的words.txt文件

create external table words(word array<string>) row format delimited collection items terminated by ' ' location '/words';

建表存储单词

create table ws(w string);

将words表中拆分出来的单词查询出来放到ws表中

insert into table ws select explode(*) from words;

统计每一个单词出现的次数

select w, count(w) from ws group by w;
1.方式二

建表管理HDFS存在的words.txt文件

create external table words(word array<string>) row format delimited collection items terminated by ' ' location '/words';

统计每一个单词出现的次数

select w, count(w) from (select explode(*) w from words)ws group by w;

UDF

  1. UDF(User-Defined Function)是Hive提供一套用于自定义函数的机制

2. 案例

  1. 写一个类继承UDF类,覆盖其中的evaluate方法
  2. 打成jar包放到Linux上
  3. 在Hive中添加jar包:add jar /home/hivedata/hive-1.0-SNAPSHOT.jar;
  4. 定义一个临时函数,并且给函数绑定类:create temporary function repeatstring as ‘cn.tedu.udf.RepeatString’;

join

  1. 如果在join的时候没有指定,那么默认就是inner join
  2. 案例

先建立orders表

create external table orders(orderid int, orderdate string, productid string, num int) row format delimited fields terminated by ’ ’ location ‘/orders’;

建立products表

create external table products(productid int, name string, price double) row format delimited fields terminated by ’ ’ location ‘/products’;

统计每一天的售出的商品的总价

select o.orderdate, sum(o.num * p.price) from orders o join products p on o.productid = p.productid group by orderdate;

  1. Hive中,支持inner/left/right/full outer join,还支持left semi join。如果a left semi join b,那么表示查询a表中哪些数据在b表中出现过:select * from products p left semi join orders o on p.productid = o.productid;

Serde

  1. Serde是Hive提供的一套针对不规则数据进行处理的机制
  2. 在Serde中,利用正则的捕获组来对应字段,正则表达式中的每一个捕获组对应了Hive表中的一个字段
  3. 案例

建表

create table logs(ip string, time string, timezone string, request_way string, resource string, protocol string, stateid int) row format serde 'org.apache.hadoop.hive.serde2.RegexSerDe' with serdeproperties ( "input.regex" = "(.*) \-\- \\[(.*) (.*)\\] \"([A-Z]*) (.*) (.*)\" ([0-9]*) \-" ) stored as textfile;

加载数据

load data local inpath ‘/home/hivedata/server.log’ into table logs;

beeline

  1. 在实际开发过程中,应该是远程连接公司的Hive服务器,那么此时就需要去进行远程访问 - beeline

步骤

1.在公司服务器的开启Hive的后台服务进程
```sh hive --service hiveserver2 &```

如果开启成功应该出现RunJar进程
1.远程连接公司的Hive服务器
sh beeline -u jdbc:hive2://10.9.162.133:10000/hivedemo -n root

视图

  1. 一个表中可以包含很多字段,但是每一个字段的使用频率并不是相同的,那么可以考虑将其中比较常用的字段抽取出来形成子表,这个子表可以用视图来表示
  2. 如果将抽取出来的视图存到磁盘上,那么此时称之为是物化视图;如果将抽取出来的视图存到内存中,那么此时称之为是虚拟视图
  3. 数据库中一般支持两种视图,但是Hive中只支持虚拟视图
  4. 建立视图:create view o_view as select orderid, num from orders;这句话表示将orders表中的orderid和num两个字段进行抽取,封装到o_view视图中
  5. 建立视图的时候封装的select语句并没有执行,而是需要在第一次使用视图的时候才会触发这句select

索引

  1. 数据库中,因为存在主键,所以会自动针对主键建立索引,数据库中采用的索引是B+tree机制。在Hive中,因为不存在主键,所以也不会自动建立索引
  2. 在Hive中可以手动建立索引,并且可以针对任意字段来建立索引
  3. 案例

建立索引表

create index o_index on table orders(orderid) as ‘org.apache.hadoop.hive.ql.index.compact.CompactIndexHandler’ with deferred rebuild in table order_index;

建立索引,索引建立完成之后不会自动更新 - 即orders表中产生变化的时候索引表不会变

alter index o_index on orders rebuild;

查询索引数据

select * from order_index;

删除索引

drop index o_index on orders;

  1. 如果一个表比较常用,那么可以针对这个表中的常用的字段来建立索引而并不是针对所有字段/表建立索引

元数据

  1. 用于描述数据的数据就是元数据
  2. Hive中的元数据包括database名、table名、字段名、分区、分桶信息等都属于元数据
    Hive中的元数据默认是存储在关系型数据中的,目前支持的元数据的数据库是Derby和MySQL,如果不指定默认使用的是Derby。因为Derby是单连接数据库,所以需要将Hive的元数据库更换为MySQL

调优

map side join:

  1. 如果一个小表和一个大表进行join查询,那么可以考虑将小表放在缓存中然后处理大表,如果需要用到小表的数据可以从内存中查询
  2. 需要执行set hive.auto.convert.join=true;才会开启小表的缓存机制
  3. 默认要求小表要放在join的左边,即小表 join 大表
  4. 默认只要小表不超过25M,那么就会放入内存中,这个大小可以通过hive.mapjoin.smalltable.filesize来设置

如果在join的时候附带了查询条件,那么考虑先用子查询执行where来降低数据量,然后再进行join

  1. group by在有多个ReduceTask的情况下可能会产生Reduce端的数据倾斜,可以通过二阶段聚合解决这个问题。在Hive中可以通过set hive.groupby.skewindata=true;来自动开启二阶段聚合
  2. distinct和聚合函数的优化,聚合函数最终只能利用一个ReduceTask来进行计算,所以如果distinct和聚合函数同时使用,那么考虑先用子查询来进行去重,最后再进行聚合
  3. 调整切片数:如果数据结构相对比较复杂或者处理逻辑相对比较复杂,那么可以考虑将切片调小来增多MapTask的数量;如果数据结构相对比较简单或者处理逻辑相对比较简单,那么可以考虑将切片调大来减少MapTask的数量 - 任务复杂就增多线程,任务简单就减少线程

数据处理流程

数据仓库和数据库

数据库数据仓库
数据量<=GB>=TB
来源相对比较单一,往往来源于单一的web应用来源相对丰富,可以是日志,爬虫,网页埋点等
种类数据结构相对单一 - 结构化数据数据结构相对丰富 - 结构化数据,半结构化数据,非结构化数据
操作提供了完整的增删改查的能力往往只提供增和查的操作而不是提供删和改的操作
事务强调事务,存在ACID四个特性不强调事务,往往是若事务甚至不支持事务
冗余精简,尽量避免冗余人为制造冗余 - 副本
数据场景往往是用于线上实时捕获数据离线历史数据和线上实时处理
系统OLTP - Online Transaction Processor 联机事务处理 - 强调事务的完整性OLAP - Online Analysis Processor - 联机分析处理 - 强调数据分析过程
面向对象程序员,DBA,运维等技术人员面向领导,市场,客户,销售等非技术人员,辅助决策

版权声明:本文为Niklaus1028原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。