一、前言
1.1 概念
Hive 依赖于 HDFS 存储数据,Hive 将 HQL 转换成 MapReduce 执行,所以说 Hive 是基于Hadoop 的一个数据仓库工具,实质就是一款基于 HDFS 的 MapReduce 计算框架,对存储在HDFS 中的数据进行分析和管理。
1.2 架构
1.用户接口:Client
CLI(hive shell)、JDBC/ODBC(java访问hive)、WEBUI(浏览器访问hive)
2.元数据:Metastore
元数据包括:表名、表所属的数据库(默认是default)、表的拥有者、列/分区字段、表的类型(是否是外部表)、表的数据所在目录等。
3.Hadoop
使用 HDFS 进行存储,使用 MapReduce 进行计算。
4.驱动器:Driver
(1)解析器(SQL Parser):将SQL字符串转换成抽象语法树AST,这一步一般都用第三方工具库完成,比如antlr;对AST进行语法分析,比如表是否存在、字段是否存在、SQL语义是否有误。
(2)编译器(Physical Plan):将AST编译生成逻辑执行计划。
(3)优化器(Query Optimizer):对逻辑执行计划进行优化。
(4)执行器(Execution):把逻辑执行计划转换成可以运行的物理计划。对于Hive来说,就是MR/Spark。
1.3 调优概述
Hive 作为大数据领域常用的数据仓库组件,在平时设计和查询时要特别注意效率。影响 Hive 效率的几乎从不是数据量过大,而是 数据倾斜、数据冗余、Job或I/O过多、MapReduce 分配不合理 等等。
对 Hive 的调优既包含 Hive 的建表设计方面,对 HiveHQL 语句本身的优化,也包含 Hive 配置参数 和 底层引擎 MapReduce 方面的调整。
所以此次调优主要分为以下四个方面展开:
1、Hive 的建表设计层面
2、HQL 语法和运行参数层面
3、Hive 架构层面
4、调优案例
1.4 调优须知
1.对于大数据计算引擎来说:数据量大不是问题,数据倾斜是个问题。
2.Hive 的复杂 HQL 底层会转换成多个 MapReduce Job 并行或者串行执行,Job 数比较多的作业运行效率相对比较低,比如即使只有几百行数据的表,如果多次关联多次汇总,产生十几个Job,耗时很长。原因是 MapReduce 作业初始化的时间是比较长的。
3.在进行 Hive 大数据分析时,常见的聚合操作比如 sum,count,max,min,UDAF等 ,不怕数据倾斜问题,MapReduce 在 Mappe 阶段的预聚合操作,使数据倾斜不成问题。
4.好的建表设计,模型设计事半功倍。
5.设置合理的 MapReduce 的 Task 并行度,能有效提升性能。(比如,10w+数据量级别的计算,用 100 个 ReduceTask,那是相当的浪费,1个足够,但是如果是亿级别的数据量,那么1个Task又显得捉襟见肘)
6.了解数据分布,自己动手解决数据倾斜问题是个不错的选择。这是通用的算法优化,但算法优化有时不能适应特定业务背景,开发人员了解业务,了解数据,可以通过业务逻辑精确有效的解决数据倾斜问题。
7.数据量较大的情况下,慎用 count(distinct),group by 容易产生倾斜问题。
8.对小文件进行合并,是行之有效的提高调度效率的方法,假如所有的作业设置合理的文件数,对任务的整体调度效率也会产生积极的正向影响。
9.优化时把握整体,单个作业最优不如整体最优。
二、Hive 建表设计层面优化
2.1 利用分区表优化
关于hive的表的类型有哪些?
1、分区表
2、分桶表
分区表:是在某一个或者几个维度上对数据进行分类存储,一个分区对应一个目录。如果筛选条件里有分区字段,那么 Hive 只需要遍历对应分区目录下的文件即可,不需要遍历全局数据,使得处理的数据量大大减少,从而提高查询效率。
也就是说:当一个 Hive 表的查询大多数情况下,会根据某一个字段进行筛选时,那么非常适合创建为分区表,该字段即为分区字段。
select1: select .... where country = "china" select2: select .... where country = "china" select3: select .... where country = "china" select4: select .... where country = "china"
分门别类:这个 city 字段的每个值,就单独形成为一个分区。其实每个分区就对应带 HDFS 的一个目录
在创建表时通过启用 partitioned by 实现,用来 partition 的维度并不是实际数据的某一列,具体分区的标志是由插入内容时给定的。当要查询某一分区的内容时可以采用 where 语句,形似 where tablename.partition_column = a 来实现。
1.创建含分区的表:
CREATE TABLE page_view( viewTime INT, userid BIGINT, page_url STRING, referrer_url STRING, ip STRING ) PARTITIONED BY(date STRING,country STRING) ROW FORMAT DELIMITED FIELDS TERMINATED BY '1' STORED AS TEXTFILE;
2.载入内容,并指定分区标志:
load data local inpath '/home/bigdata/pv_2018-07-08_us.txt' into table page_view partition(date='2018-07-08', country='US');
3.查询指定标志的分区内容:
SELECT page_views.* FROM page_views WHERE page_views.date >= '2008-03-01' AND page_views.date <= '2008-03-31' AND page_views.referrer_url like '%xyz.com';
简单总结:
1、当你意识到一个字段经常用来做where,建分区表,使用这个字段当做分区字段
2、在查询的时候,使用分区字段来过滤,就可以避免全表扫描。只需要扫描这张表的一个分区的数据即可
2.2 利用分桶表优化
跟分区的概念很相似,都是把数据分成多个不同的类别,区别就是规则不一样!
1、分区:
按照字段值来进行:一个分区,就只是包含这个这一个值的所有记录不是当前分区的数据一定不在当前分区当前分区也只会包含当前这个分区值的数据
2、分桶:默认规则:Hash散列一个分桶中会有多个不同的值如果一个分桶中,包含了某个值,这个值的所有记录,必然都在这个分桶
Hive Bucket,分桶,是指将数据以指定列的值为 key 进行 hash,hash 到指定数目的桶中,这样做的目的和分区表类似,使得筛选时不用全局遍历所有的数据,只需要遍历所在桶就可以了。这样也可以支持高效采样。
1、采样
2、join
如下例就是以 userid 这一列为 bucket 的依据,共设置 32 个 buckets
CREATE TABLE page_view( viewTime INT, userid BIGINT, page_url STRING, referrer_url STRING, ip STRING ) COMMENT 'This is the page view table' PARTITIONED BY(dt STRING, country STRING) CLUSTERED BY(userid) SORTED BY(viewTime) INTO 32 BUCKETS ROW FORMAT DELIMITED FIELDS TERMINATED BY '1' COLLECTION ITEMS TERMINATED BY '2' MAP KEYS TERMINATED BY '3' STORED AS SEQUENCEFILE;
分桶的语法:
CLUSTERED BY(userid) SORTED BY(viewTime) INTO 32 BUCKETS
CLUSTERED BY(userid) 表示按照 userid 来分桶
SORTED BY(viewTime) 按照 viewtime 来进行桶内排序
INTO 32 BUCKETS 分成多少个桶
两个表以相同方式(相同字段)划分桶,两个表的桶个数是倍数关系
create table order(cid int,price float) clustered by(cid) into 32 buckets; create table customer(id int,first string) clustered by(id) into 32 buckets; select price from order t join customer s on t.cid = s.id;
通常情况下,Sampling 在全体数据上进行采样,这样效率自然就低,它要去访问所有数据。而如果一个表已经对某一列制作了 bucket,就可以采样所有桶中指定序号的某个桶,这就减少了访问量。
如下例所示就是采样了 page_view 中 32 个桶中的第三个桶的全部数据:
SELECT * FROM page_view TABLESAMPLE(BUCKET 3 OUT OF 32);
如下例所示就是采样了 page_view 中 32 个桶中的第三个桶的一半数据:
SELECT * FROM page_view TABLESAMPLE(BUCKET 3 OUT OF 64);
总结三种采样方式
# 分桶抽样: select * from student tablesample(bucket 3 out of 32); # 随机采样:rand() 函数 select * from student order by rand() limit 100; // 效率低 select * from student distribute by rand() sort by rand() limit 100; // 推荐使 # 用这种 # 数据块抽样:tablesample()函数 select * from student tablesample(10 percent); # 百分比 select * from student tablesample(5 rows); # 行数 select * from student tablesample(5 M); # 大小
2.3 选择合适的文件存储格式
在 HiveSQL 的 create table 语句中,可以使用 stored as … 指定表的存储格式。Apache Hive 支持 Apache Hadoop 中使用的几种熟悉的文件格式,比如 TextFile、SequenceFile、RCFile、Avro、ORC、ParquetFile 等。
存储格式一般需要根据业务进行选择,在我们的实操中,绝大多数表都采用 TextFile 与Parquet 两种存储格式之一。
TextFile 是最简单的存储格式,它是纯文本记录,也是 Hive 的默认格式。虽然它的磁盘开销比较大,查询效率也低,但它更多地是作为跳板来使用。
RCFile、ORC、Parquet 等格式的表都不能由文件直接导入数据,必须由 TextFile 来做中转。 Parquet 和 ORC 都是 Apache 旗下的开源列式存储格式。列式存储比起传统的行式存储更适合批量 OLAP 查询,并且也支持更好的压缩和编码。
创建表时,特别是宽表,尽量使用 ORC、ParquetFile 这些列式存储格式,因为列式存储的表,每一列的数据在物理上是存储在一起的,Hive 查询时会只遍历需要列数据,大大减少处理的数据量。
第一种: [*] TextFile
1、存储方式:行存储。默认格式,如果建表时不指定默认为此格式。,
2、每一行都是一条记录,每行都以换行符"\n"结尾。数据不做压缩时,磁盘会开销比较大,数据解析开销也比较大。
3、可结合Gzip、Bzip2等压缩方式一起使用(系统会自动检查,查询时会自动解压),推荐选用可切分的压缩算法(bzip2)。
第二种:Sequence File
1、一种Hadoop API提供的二进制文件,使用方便、可分割、个压缩的特点。
2、支持三种压缩选择:NONE、RECORD、BLOCK。RECORD压缩率低,一般建议使用BLOCK压缩。
第三种:RC File
1、存储方式:数据按行分块,每块按照列存储 。
A、首先,将数据按行分块,保证同一个record在一个块上,避免读一个记录需要读取多个block。
B、其次,块数据列式存储,有利于数据压缩和快速的列存取。
2、相对来说,RCFile对于提升任务执行性能提升不大,但是能节省一些存储空间。可以使用升级版的ORC格式。
第四种:ORC File
1、存储方式:数据按行分块,每块按照列存储
2、Hive提供的新格式,属于RCFile的升级版,性能有大幅度提升,而且数据可以压缩存储,压缩快,快速
列存取。
3、ORC File会基于列创建索引,当查询的时候会很快
第五种:Parquet File
1、存储方式:列式存储。
2、Parquet对于大型查询的类型是高效的。对于扫描特定表格中的特定列查询,Parquet特别有用。
Parquet一般使用Snappy、Gzip压缩。默认Snappy。
3、Parquet支持Impala 查询引擎。
4、表的文件存储格式尽量采用Parquet或ORC,不仅降低存储量,还优化了查询,压缩,表关联等性能。
2.4 选择合适的压缩格式
Hive 语句最终是转化为 MapReduce 程序来执行的,而 MapReduce 的性能瓶颈在与 网络 IO 和 磁盘 IO,要解决性能瓶颈,最主要的是 减少数据量,对数据进行压缩是个好方式。压缩虽然是减少了数据量,但是压缩过程要消耗 CPU,但是在 Hadoop 中,往往性能瓶颈不在于 CPU,CPU 压力并不大,所以压缩充分利用了比较空闲的 CPU。
常用压缩方法对比
如何选择压缩方式
1、压缩比率
2、压缩解压速度
3、是否支持split
支持分割的文件可以并行的有多个 mapper 程序处理大数据文件,大多数文件不支持可分割是因为这些文件只能从头开始读。
是否压缩
1、计算密集型,不压缩,否则进一步增加了CPU的负担
2、网络密集型,推荐压缩,减小网络数据传输
各个压缩方式对应的class类
压缩使用
## 默认值是false set mapreduce.output.fileoutputformat.compress=true; ## 默认值是Record set mapreduce.output.fileoutputformat.compress.type=BLOCK ## 默认值是org.apache.hadoop.io.compress.DefaultCodec set mapreduce.output.fileoutputformat.compress.codec=org.apache.hadoop.io.compress.GzipCodec
Map 输出结果也以 Gzip 进行压缩
## 启用map端输出压缩 set mapred.map.output.compress=true ## 默认值是org.apache.hadoop.io.compress.DefaultCodec set mapreduce.map.output.compress.codec=org.apache.hadoop.io.compress.GzipCodec
对 Hive 输出结果和中间都进行压缩
## 默认值是false,不压缩 set hive.exec.compress.output=true ## 默认值是false,为true时MR设置的压缩才启用 set hive.exec.compress.intermediate=true