文章目录
背景
- 引入原因:
1. – 对存在HDFS上的文件或HBase中的表进行查询时,是要手工写一堆MapReduce代码
2. 对于统计任务,只能由动MapReduce的程序员才能搞定
3. 耗时耗力,更多精力没有有效的释放出来 - Hive基于一个统一的查询分析层,通过SQL语句的方式对HDFS上的数据进行查询、统计和分析
1.数据库的原理
其实通常所说的将数据存储到数据库,并不是真的存储到数据库,数据库不过是在你和你的磁盘间弄了个软件层,其实你的数据还是被作为源数据存储到你本地的磁盘上了,但是只有源数据还不够,他还存储了元数据(数据库存储结构定义信息(库,表,列,定义信息)
2.什么是Hive
- Hive是一个SQL解析引擎,将SQL语句转译成MR Job,然后再Hadoop平台上运行,达到快速开发的目的。
- Hive中的表是纯逻辑表,就只是表的定义等,即表的元数据。本质就是Hadoop的目录/文件,达到了元数据与数据存储分离的目的
- Hive本身不存储数据,它完全依赖HDFS和MapReduce。
- Hive的内容是读多写少,不支持对数据的更新
- Hive中没有定义专门的数据格式,由用户指定,需要指定三个属性:
1. 列分隔符
2. 行分隔符
3. 读取文件数据的方法
3.HQL与传统SQL的区别
| HQL | SQL | |
|---|---|---|
| 数据存储 | HDFS(源数据)、MySql或者Derby(元数据) | local FS |
| 数据格式 | 用户自定义 | 系统决定 |
| 数据更新 | 不支持 | 支持 |
| 索引 | 有 | 有 |
| 执行 | MapReduce | Executor |
| 数据规模 | 大 | 小 |
| 数据检查 | 读时模式 | 写时模式 |
什么是读时模式?
很少情况下往hive中加载的数据是比较规整的,字段与字段之间都是分割好的,每一个字段都不是脏数据,并且每一个字段都是有意义的。
但是在真实场景中不见得这个尽人意,当你把不符合某个表规范的脏数据插入到这个表中,不会出错,但是当你读这个表的时候,脏数据读出的是NULL。
4. Hive的原理
4.1 Hive简介
hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供sql查询功能,可以将sql语句转换为MapReduce任务进行运行。 其优点是学习成本低,可以通过类SQL语句快速实现简单的MapReduce统计,不必开发专门的MapReduce应用,十分适合数据仓库的统计分析。其本质还是在HDFS上执行MapReduce,虽然简化了操作,但是执行效率会相对减弱。
Hive数据仓库中包含着解释器、编译器、优化器等,这些是其独有的部分,用于将sql语句变成MapReduce去执行,后续的工作就自动交给了Hadoop、Yarn和MapReduce,Hive在运行时,实际的源数据存在与HFDS上,而描述数据的元数据存放在关系型数据库中。
有人说hive既能存储也能计算,这句话也对也不对,因为hive只是通过解释器、编译器、优化器来把SQL变为MR,它的存储也是交给了HDFS的。
- Hive数据仓库完成解析SQL、优化SQL、策略选择、模型消耗;客户端将sql语句交给Hive,Hive负责值使相对应的MapReduce执行。
- 存在于关系型数据库中的元数据包含着行的分隔符、字段的分隔符、字段的类型、字段的名称等信息。
5.Hive体系架构
5.1 Hive的基本组成
用户接口主要有三个:CLI,Client 和 WUI。 元数据存储:通常是存储在关系数据库如 mysql , derby中。 语句转换:解释器、编译器、优化器、执行器。5.2 各组件的基本功能
用户接口主要由三个:CLI、JDBC/Beeline和WebGUI。其中,CLI为shell命令行;JDBC/Beeline是Hive的JAVA实现,与传统数据库JDBC类似;WebGUI是通过浏览器访问Hive。
元数据存储:Hive 将元数据存储在数据库中(MySql或者Derby)。Hive中的元数据包括表的名字,表的列和分区及其属性,表的属性(是否为外部表等),表的数据所在目录等。
解释器、编译器、优化器完成 HQL 查询语句从词法分析、语法分析、编译、优化以及查询计划的生成。生成的查询计划存储在HDFS 中,并在随后有 MapReduce 调用执行。
5.3 Hive和Hadoop的关系
5.4 Hive的数据管理
- hive的表本质就是Hadoop的目录/文件,由配置文件设置目录。
-hive默认表存放路径一般都是在你工作目录的hive目录里面,按表名做文件夹分开,如果你有分区表的话,分区值是子文件夹,可以直接在其它的M/R job里直接应用这部分数据
- Hive中所有的数据都存储在 HDFS 中,没有专门的数据存储格式
- 只需要在创建表的时候告诉 Hive 数据中的列分隔符和行分隔符,Hive 就可以解析数据。
5.4.1 Hive中的内部表和外部表
- Hive的create创建表的时候,选择的创建方式:
- create table(内部)
建表:
CREATE TABLE zxjtb1(
id INT,
name STRING,
age INT,
gfs ARRAY<STRING>,
address MAP<STRING,STRING>,
info STRUCT<country:String,province:String,shi:String>
)
ROW FORMAT DELIMITED -- 行格式分割
FIELDS TERMINATED BY ' ' -- 字段的分割符
COLLECTION ITEMS TERMINATED BY ',' -- 集合元素间的分割符
MAP KEYS TERMINATED BY ':' -- Map中key-value的分隔符
LINES TERMINATED BY '\n'; -- 行与行分隔符
创建表的方式:
create table zxj1 like zxjtb1; -- 只是创建表结构
create table zxj2 AS SELECT* from zxjtb1;--会创建相应的表结构,并且插入数据
插入数据的方式:
INSERT INTO zxjtb1 (列1, 列2,...) VALUES (值1, 值2,....);
load data local inpath '/root/gfs.txt' into table zxjtb1; -- 实质上是把文件直接上传到HDFS
from zxj2 insert into table zxjtb1 select *; -- 查询其他表数据 insert 到新表中
- create external table(外部)
建表:
create external table wc_external
(word1 STRING,
word2 STRING
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ' '
location '/test/external';
创建表方式,插入数据方式和上面相同
-
特点:
- 在导入数据到外部表,数据并没有移动到自己的数据仓库目录下,也就是说外部表中的数据并不是由它自己来管理的!而表则不一样;
- 在删除内表的时候,Hive将会把属于表的元数据和数据全部删掉;而删除外部表的时候,Hive仅仅删除外部表的元数据,HDFS数据是不会删除的。
5.4.2 Hive中的临时表
- Hive的create创建临时表:
create TEMPORARY table ttabc(id Int,name String) - 临时表不支持分区字段和创建索引
- 每次退出Hive脚本时都会清空此次创建的临时表
5.4.3 Hive中的分区表
- 在 Hive 中,表中的一个 Partition 对应于表下的一个目录,所有的 Partition 的数据都存储在对应的目录中
- 例如:pvs 表中包含 ds 和 city 两个 Partition,则
- 对应于 ds = 20090801, ctry = US 的 HDFS 子目录为:/wh/pvs/ds=20090801/ctry=US;
- 对应于 ds = 20090801, ctry = CA 的 HDFS 子目录为;/wh/pvs/ds=20090801/ctry=CA;
- partition是辅助查询,缩小查询范围,加快数据的检索速度和对数据按照一定的规格和条件进行管理。
- 分区表(防止暴力扫描全表)又分为:静态分区表和动态分区表
创建静态单分区表:
create table day_table (
id int,
content string
)
partitioned by (dt string) -- 按照dt分区
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t' ;
创建静态多分区表:
create table day_hour_table (
id int,
content string
)
partitioned by (dt int,hour int) -- 按照dt,hour分区
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t' ;
静态分区表加载数据必须指定分区:
insert into day_table partition (dt = "9-26") values(1,"anb");
insert into day_hour_table partition(dt=9,hour=1) values(1,"a2 bc");
load data local inpath "/root/ceshi" into table day_table partition (dt="9-27");
load data local inpath "/root/ceshi" into table day_table partition (dt=10,hour=10);
删除分区:
ALTER TABLE day_table DROP PARTITION (dt="9-27");
ALTER TABLE day_table DROP PARTITION (dt=10,hour=10);
查询表的分区:
SHOW PARTITIONS table_name;
创建动态分区表
刚才分区表示静态分区表,一个文件数据只能导入到某一个分区中,并且分区是用户指定的;这种方式不够灵活,业务场景比较局限;动态分区可以根据数据本身的特征自动来划分分区,比如我们可以指定按照数据中的年龄、性别来动态分区会产出3个不同的分区 .
创建动态分区表之前的操作
开启支持动态分区
set hive.exec.dynamic.partition=true;
set hive.exec.dynamic.partition.mode=nonstrict;
静态分区与动态分区创建表的语句是一模一样的
CREATE TABLE gfstbl_dynamic(
id INT,
name STRING,
gfs ARRAY<STRING>,
address MAP<STRING,STRING>,
info STRUCT<country:String,province:String,shi:String>
)
partitioned by (sex string,age INT)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ' '
COLLECTION ITEMS TERMINATED BY ','
MAP KEYS TERMINATED BY ':'
LINES TERMINATED BY '\n';
动态分区表加载数据:(不可以使用load,它只是将数据上传到HDFS指定目录中,而动态分区是自动分区的)
from gfstbl_pt
insert into gfstbl_dynamic partition(sex,age)
select id,name,gfs,address,info,sex,age;
查看分区数:
show partitions gfstbl_dynamic;
5.4.4 Hive中的分桶表
- 分桶表是对数据进行哈希取值,然后放到不同文件中存储。
- Bucket主要作用:
1. join
2. 随机抽样
之前要开启分桶表的支持:set hive.enforce.bucketing=true;
创建分桶表
create table bucket_user (id int,name string)
clustered by (id) into 4 buckets
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';
插入数据:
insert into table bucket_user partition(id) select id, name from original;
抽样:
select * from bucket_user tablesample(bucket x out of y on id);
y必须是table总bucket数的倍数或者因子。
x表示从哪个bucket开始抽取。
分桶数/y 指的是抽取几个桶的数据。
例如,table总bucket数为32,tablesample(bucket 3 out of 16),表示总共抽取(32/16=)2个bucket的数据,分别为第3个bucket和第(3+16=)19个bucket的数据。
分桶+分区表
CREATE TABLE psnbucket_partition( id INT, name STRING, age INT)
PARTITIONED BY(height DOUBLE)
CLUSTERED BY (age) INTO 4 BUCKETS
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';
5.4.5 Hive中的视图
  为什么要使用视图?
视图中保存的是一推复杂的SQL语句,视图是一个懒执行,只有我们用到此视图的时候才会执行此复杂的SQL语句;可以将这么长的SQL(数据表)与视图对应映射,每次查询这个视图就是执行了长的SQL语句。
- 特点:
- 不支持物化视图(保存在磁盘上)
- 只能查询,不能做加载数据操作 load data into
- 视图的创建,只是保存一份元数据,查询视图时才执行对应的子查询
- view定义中若包含了ORDER BY/LIMIT语句,当查询视图时也进行ORDER BY/
- 一旦创建成功,无法修改
- 创建视图
CREATE VIEW IF NOT EXISTS view1 AS SELECT * FROM logtbl order by age;
-创建视图的时候不会启动MR任务 -
但是在查询视图的时候会启动MR任务`select * from view1;` - show tables可以查看已经创建的视图
- drop view view1 删除视图
5.4.6 Hive中的索引
为什么要使用索引?
Hive的索引目的是提高Hive表指定列的查询速度。索引就类似目录。
没有索引时,类似’WHERE tab1.col1 = 10’ 的查询,Hive会加载整张表或分区,然后处理所有的rows,
但是如果在字段col1上面存在索引时,那么只会加载和处理文件的一部分。
与其他传统数据库一样,增加索引在提升查询速度时,会消耗额外资源去创建索引表和需要更多的磁盘空间存储索引。
他会把索引列的每个数据都建立一个目录,说明它所在的位置。
创建索引库,用于存放索引
create index t2_index on table psnbucket_partition(age)
as 'org.apache.hadoop.hive.ql.index.compact.CompactIndexHandler' with deferred rebuild
in table t2_index_table;
上述是对 psnbucket_partition表的age字段设置索引 t2_index,索引的内容放入 t2_index_table;
这一步是真正的创建索引信息,并且存储到索引库中,若数据库有新增数据,也可以使用以上语句重建索引
alter index t2_index on psnbucket_partition rebuild;
5.4.7 Hive数据类型
- 数据类型
1. TINYINT -128~127
2. SMALLINT -32,768~32767
3. INT -2,147,483,648~2147483647
4. FLOAT
5. DOUBLE
6. STRING 字符串
7. VARCHAR 可变长度字符串
8. CHAR 字符
9. ARRAYS 数组类型
10. MAPS 键值对集合类型
11. STRUCTS 结构化类型
5.4.8 总结
索引和分区最大的区别就是索引不分割数据库,分区分割数据库。
索引其实就是拿额外的存储空间换查询时间,但分区已经将整个大数据库按照分区列拆分成多个小数据库了。
分区和分桶最大的区别就是分桶随机分割数据库,分区是非随机分割数据库
因为分桶是按照列的哈希函数进行分割的,相对比较平均;而分区是按照列的值来进行分割的,容易造成数据倾斜。
其次两者的另一个区别就是分桶是对应不同的文件(细粒度),分区是对应不同的文件夹(粗粒度)。
注意:普通表(外部表、内部表)、分区表这三个都是对应HDFS上的目录,桶表对应是目录里的文件
5.5 Hive的调优
核心思想:把Hive SQL当作Map Reduce程序去优化
但是不是所有的SQL都会被转化为MapReduce来执行
- select仅查询本表字段
- where只对本表字段做条件过滤
5.5.1 Hive的运行方式
- 本地模式
-
开启本地模式
set hive.exec.mode.local.auto=true; -
当满足以下条件时才会真正的使用本地模式,否则使用集群模式(总体来说适用于少量数据时)
1. 输入数据大小必须小于参数
2. map数必须小于参数
3. reduce数必须==0|1
-
开启本地模式
- 集群模式
5.5.2 并行计算
- 并行模式的开启
set hive.exec.parallel=true; - 一次SQL计算中所允许的最大的Map个数
hive.exec.parallel.thread.number;
5.5.3 严格模式
- 设置严格模式增加查询的效率
set hive.mapred.mode=strict;(默认是非严格模式) - 严格模式要求
1. 对于分区表,必须添加where对于分区字段的条件过滤
2. order by语句必须包含limit限制
3. 限制笛卡尔积的查询(效率太低)
5.5.4 Hive join
- join计算时,将小表(驱动表)放在join左边
- Map join:在Map端完成Join
- 实现方式:
- SQl方式:在SQl语句中添加Map join标记
SELECT /*+ MAPJOIN(smallTable) */ smallTable.key, bigTable.value FROM smallTable JOIN bigTable ON smallTable.key = bigTable.key - 开启自动的Map join
set hive.auto.convert.join = true;
- SQl方式:在SQl语句中添加Map join标记
- 实现方式:
5.5.5 Map-Side聚合
- 设置以下开启在Map端额聚合combiner
set hive.map.aggr=true; - 相关的配置参数
1. hive.groupby.mapaggr.checkinterval: 默认100000;map端group by执行聚合后产生的最大记录数(默认:100000)
2. hive.map.aggr.hash.min.reduction:默认0.5 ;进行聚合的最小比例
3. hive.map.aggr.hash.percentmemory: 默认0.5 ;map端聚合使用的内存占总内存的比例
4. hive.map.aggr.hash.force.flush.memory.threshold:默认0.9 ;
5. map端做聚合操作是hash表的最大可用内容,大于该值则会触发flush;map端做聚合操作是hash表的最大可用内容,大于该值则会触发flush
6. hive.groupby.skewindata;是否对GroupBy产生的数据倾斜做优化,默认为false
5.5.6 控制Hive中Map以及Reduce的数量
- Map数量相关的参数
- mapred.max.split.size一个split的最大值,即每个map处理文件的最大值
- mapred.min.split.size.per.node一个节点上split的最小值
- mapred.min.split.size.per.rack;一个机架上split的最小值
- Reduce数量相关的参数
- mapred.reduce.tasks;强制指定reduce任务的数量
- hive.exec.reducers.bytes.per.reducer 默认 256M;每个reduce任务处理的数据量
- hive.exec.reducers.max 默认1009;每个任务最大的reduce数
5.5.7 Hive-JVM重用
- 适用场景:
- 小文件个数过多
- task个数过多
- 通过
set mapred.job.reuse.jvm.num.tasks=n;来设置,n为task插槽个数
5.5.8 Hive优化—数据倾斜
- 操作
- Join
- Group by
- Count Distinct
- 原因
- key分布不均导致的
- 人为的建表疏忽
- 业务数据特点
- 症状
- 任务进度长时间维持在99%(或100%),查看任务监控页面,发现只有少量(1个或几个)reduce子任务未完成。
- 查看未完成的子任务,可以看到本地读写数据量积累非常大,通常超过10GB可以认定为发生数据倾斜。
- 倾斜度
- 平均记录数超过50w且最大记录数是超过平均记录数的4倍。
- 最长时长比平均时长超过4分钟,且最大时长超过平均时长的2倍。
- 万能方法
- hive.groupby.skewindata=true
- hive.groupby.skewindata=true
6. Hive环境搭建和部署
6.0搭建集群的模式有三种
6.1 本地模式(derby)
不支持多用户操作