一、 视图
1. 什么是视图
- 逻辑窗口、视图
2. 视图的特性
- 只有逻辑视图,没有物化视图
- 视图只能查询,不能LOAD/INSERT/
- 视图在创建时候,只是保存了一份元数据,当查询视图的时候,才开始执行视图对应的那些子查询
3. 优点
- 通过引入视图机制,用户可以将注意力集中在其关心的数据上(而非全部数据),这样就大大提高了用户效率与用户满意度,而且如果这些数据来源于多个基本表结构,或者数据不仅来自于基本表结构,还有一部分数据来源于其他视图,并且搜索条件又比较复杂时,需要编写的查询语句就会比较烦琐,此时定义视图就可以使数据的查询语句变得简单可行
- 定义视图可以将表与表之间的复杂的操作连接和搜索条件对用户不可见,用户只需要简单地对一个视图进行查询即可,故增加了数据的安全性,但不能提高查询效率
4. 例子
- 创建视图
CREATE VIEW student_view
AS SELECT * FROM students where score>85 ;
- 查看视图
DESC student_view;
- 删除视图
DROP VIEW student_view;
二、 索引
1.索引概述
- Hive索引的目标是提高表的某些列的查询查询速度。如果没有索引,带有谓词的查询(如’WHERE tab1.col1 = 10’)会加载整个表或分区并处理所有行。但是如果col1存在索引,则只需要加载和处理文件的一部分
- 索引可以提供的查询速度的提高是以创建索引和存储索引的磁盘空间的额外处理为代价的
2. 使用场景
适用于不更新的静态字段。以免总是重建索引数据。每次建立、更新数据后,都要重建索引以构建索引表
3. 索引机制
- hive在指定列上建立索引,会产生一张索引表(Hive的一张物理表),里面的字段包括,索引列的值、该值对应的HDFS文件路径、该值在文件中的偏移量
- v0.8后引入bitmap索引处理器,这个处理器适用于排重后,值较少的列(例如,某字段的取值只可能是几个枚举值)
- 因为索引是用空间换时间,索引列的取值过多会导致建立bitmap索引表过大
4. 如何创建索引
4.1 创建、显示和删除索引
- 创建索引
create index student_index on table student(age) as 'compact' with deferred rebuild;
- 查看索引
show index on student;
- 删除索引
drop index student_index on student;
4.2 创建然后构建、显示格式化(带列名称)和删除索引
- 创建索引
create index student_index on table student(id)as 'compact' with deferred rebuild;
- 重建索引
alter index student_index on student rebuild;
- 查看索引
show formatted index on student;
- 删除索引
drop index student_index on student;
4.3 创建位图索引、构建、显示和删除
- 创建位图索引
create index student_index on table student(name) as 'bitmap' with deferred rebuild;
- 重建索引
alter index student_index on student rebuild;
- 查看索引
show formatted index on student;
- 删除索引
drop index student_index on student;
4.4在新表中创建索引、删除
- 新表中创建索引
create index student_index on table student(age) as 'compact' with deferred rebuild in table student_index_table;
- 删除索引
drop index student_index on student;
4.5 创建存储为rcfile的索引
- 创建rccfile索引
create index student_index on table student(name) as 'compact' with deferred rebuild stored as rcfile;
- 删除索引
drop index student_index on student;
4.6 创建存储为textfile的索引
- 创建textfile索引
CREATE INDEX student_index ON TABLE student (age) AS 'COMPACT' with deferred rebuild ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' STORED AS TEXTFILE;
- 删除索引
drop index student_index on student;
4.7 使用索引属性创建索引
- 使用索引属性创建索引
CREATE INDEX student_index ON TABLE student (age) AS 'COMPACT' with deferred rebuild IDXPROPERTIES ("prop1"="value1", "prop2"="value2");
- 删除索引
drop index student_index on student;
4.8 使用表属性创建索引
- 使用表属性创建索引
CREATE INDEX student_index ON TABLE student (age) AS 'COMPACT' with deferred rebuild TBLPROPERTIES ("prop3"="value3", "prop4"="value4");
-删除索引
drop index student_index on student;
三、 数据倾斜
1. 什么是数据倾斜?
由于数据分布不均匀,造成数据大量的集中到一点,造成数据热点
2. Hadoop框架的特性
- 不怕数据大,怕数据倾斜
- Jobs 数比较多的作业运行效率相对比较低,如子查询比较多
- SUM,COUNT,MAX,MIN 等聚集函数,不会有数据倾斜问题
3. 容易数据倾斜情况
- 小表关联超大表 JOIN
- GROUP BY 不和聚集函数搭配使用的时候
- COUNT(DISTINCT),在数据量大的情况下,容易数据倾斜,因为 COUNT(DISTINCT)是按 GROUP BY字段分组,按 DISTINCT 字段排序
总结
4. 产生数据倾斜的具体原因
- key 分布不均匀,少量的key 对应大量value
- 业务数据本身的特性
- 建表考虑不周全
- 某些 HQL 语句本身就存在数据倾斜
5. 主要表现
任务进度长时间维持在 99% 或者 100% 的附近, 查看任务监控页面,发现只有少量(1个或者几个) reduce 子任务未完成, 因为其处理的数据量和其他的 reduce 差异过大。 单一 reduce 处理的记录数和平均记录数相差太大,通常达到3倍甚至更多,最长时间远大于平均时长
6. 业务场景
6.1 空值产生的数据倾斜
场景说明
在日志中,常会有信息丢失的问题, 比如日志中的 user_id,如果取其中的 userid 和用户表中的 userid 相关联,就会碰到数据倾斜的问题
解决方案
解决方案 1: user_id 为空的不参与关联
SELECT * FROM log a
JOIN user b
ON a.user_id IS NOT NULL
AND a.user_id = b.user_id
UNION ALL
SELECT * FROM log c WHERE c.user_id IS NULL;
解决方案 2: 赋予空值新的 key 值
SELECT * FROM log a
LEFT OUTER JOIN user b
ON CASE WHEN a.user_id IS NULL THEN CONCAT('hive',RAND())
ELSE a.user_id END = b.user_id;
总结
方法 2 比方法 1 效率更好, 不但 IO 少了,而且作业数也少了,方案 1 中, log 表读了两次, jobs 肯定是 2,而方案 2 是 1。 这个优化适合无效 id(比如-99, ‘’, null)产生的数据倾斜, 把空值的 key 变成一个字符串加上一个随机数,就能把造成数据倾斜的数据分到不同的 reduce 上解决数据倾斜的问题
6.2 不同数据类型关联产生数据倾斜
场景说明
用户表中 user_id 字段为 int, log 表中 user_id 为既有 string 也有 int 的类型。当按照两个表的 user_id 进行 Join 操作的时候,默认的 hash 操作会按照 int 类型的 id 来进行分配, 这样就会导致所有的 string 类型的 id 就被分到同一个 reducer 当中
解决方案
把数字类型 id 转换成 string 类型的 id
SELECT * FROM user a
LEFT OUTER JOIN log b
ON b.user_id = CAST(a.user_id AS STRING);
6.3 大小表关联查询
解决方案
使用 MapJoin 解决小表关联大表造成的数据倾斜问题
MapJoin 概念
将其中的某个表(全量数据)分发到所有 Map 端进行 Join,从而避免了reduce,前提要求是内存足以装下该全量数据
总结
- 以大表 a 和小表 b 为例,所有的 MapTask 节点都装载小表 b 的所有数据,然后大表 a 的一个数据块数据比如说是 a1, 去跟 b 全量数据做连接, 就省去了 reduce 来做汇总的过程
- 所以相对来说,在内存允许的条件下使用 MapJoin 比直接使用 MapReduce 效率还高些,当然这只限于做 Join 查询的时候
- 如果是大大表关联呢?那就大事化小,小事化了。把大表切分成小表,然后分别 MapJoin
四、 Hive执行过程案例分析
1. 例子
SELECT pv.pageid, u.age FROM page_view pv
JOIN user u
ON (pv.userid = u.userid);
2. 实现过程
- map
- 以 JOIN ON 条件中的列作为 Key,如果有多个列,则 Key 是这些列的组合
- 以 JOIN 之后所关心的列作为 Value,当有多个列时, Value 是这些列的组合。在Value 中还会包含表的 Tag 信息,用于标明此 Value 对应于哪个表
- 按照 Key 进行排序
- shuffle
- 根据 Key 的值进行 Hash,并将 Key/Value 对按照 Hash 值推送至不同的 Reduce 中
- reduce
- Reducer 根据 Key 值进行 Join 操作,并且通过 Tag 来识别不同的表中的数据
- 语句
GROUP BY
SELECT pageid, age, count(1) FROM pv_users
GROUP BY pageid, age;
五. Hive优化策略
1. Hadoop框架计算特性
- 数据量大不是问题,数据倾斜是个问题
- jobs 数比较多的作业运行效率相对比较低
- SUM,COUNT,MAX,MIN 等 UDAF,不怕数据倾斜问题,Hadoop在Map端的汇总合并优化,使数据倾斜不成问题。
- COUNT(DISTINCT)是按GROUP BY字段分组,按 DISTINCT字段排序,一般这种分布方式是很容易倾斜
- 例如:男 uv、女 uv,淘宝一天 30 亿的 pv,如果按性别分组,分配 2 个 reduce,每个 reduce 处理 15 亿数据。
2. 常用优化手段
- 好的模型设计事半功倍
- 解决数据倾斜问题
- 减少 job 数
- 设置合理的 MapReduce 的 task 数
- 了解数据分布
- 慎用 COUNT(DISTINCT), GROUP BY
- 对小文件进行合并
- 把握整体
3. 全排序
- CLUSTER BY: 对同一字段分桶并排序,不能和 SORT BY 连用
- DISTRIBUTE BY: 分桶,保证同一字段值只存在一个结果当中
- SORT BY: 单机排序,单个 Reduce 结果
- 全局排序
4. 笛卡尔积
- Hive 对笛卡尔积支持较弱
- MapJoin 才是最好的解决办法
5. IN/EXISTS语句
虽然经过测验, Hive 1.2.1 也支持 IN 操作,但还是推荐使用 Hive 的一个高效替代方案: LEFT SEMI JOIN
6. Map个数
Map个数的决定因素
是不是Map数越多越好?
是不是保证每个Map处理接近128M的文件块,就高枕无忧了?
针对上面的问题,我们需要采取两种方式来解决:即减少Map数和增加Map数。
总结:如果文件大于块大小,那么会拆分,如果小于块大小,则把该文件当成一个块
7. Reduce个数
怎样设置Reduce的个数
Reduce的个数是不是越多越好?
什么情况下只有一个Reduce?
8. JOIN
Join 原则: 在使用写有 Join 操作的查询语句时有一条原则,应该将条目少的表/子查询放在 Join 操作符的左边。
如果 Join 的 key 相同,不管有多少个表,都会则会合并为一个 MapReduce,而不是 ‘n’ 个。在做 OUTER JOIN 的时候也是一样
9. GROUP BY
Map 端部分聚合
并不是所有的聚合操作都需要在 Reduce 端完成,很多聚合操作都可以先在 Map 端进行部分聚合,最后在 Reduce 端得出最终结果
MapReduce 的 combiner 组件
\hive.map.aggr = true ##是否在 Map 端进行聚合,默认为 True
hive.groupby.mapaggr.checkinterval = 100000 ##在 Map 端进行聚合操作的条目数目
10. 小文件合并
文件数目过多,会给 HDFS 带来压力,并且会影响处理效率,可以通过合并 Map 和 Reduce 的结果文件来消除这样的影响
hive.merge.mapfiles = true ##是否和并 Map 输出文件,默认为 True
hive.merge.mapredfiles = false ##是否合并 Reduce 输出文件,默认为 False
hive.merge.size.per.task = 256*1000*1000 ##合并文件的大小