判断一种压缩协议好与坏的标准: 压缩后文件占比, 解压速度, 压缩速度.
Hive的底层是MR程序, 所以Hive的压缩本质上还是MR的压缩, 它(MR程序)支持GZip, Bzip2, Lzo, Snappy(推荐使用: 因为更均衡)等协议.
数据压缩分为两项, 即: Map端压缩 和 Reduce端压缩.
Hive表的存储格式分为 行存储 和 列存储两种, 其中:
行存储主要有两种方式: TextFile(默认的), SequenceFile
列存储主要有两种方式: Orc(推荐, 更均衡), Parquet(Spark中用的较多)
优点: select * from … 方式, 查询数据, 效率较高.
缺点:
优点:
缺点: select * 方式效率相对较低, 但是实际开发中, 这种写法几乎不用.
细节:
我们知道Hive的底层要转MR任务来执行, 而MR程序的计算速度是非常慢的, 如果执行的是简单的HiveSQL, 没必要转MR程序, 直接执行即可.
大白话, Fetch抓取的意思是: HiveSQL 底层能不转MR, 就不转MR, 而是直接执行.
哪些SQL不会转MR程序的呢?
通过 set hive.fetch.task.conversion=值; 的方式, 可以设置本地抓取模式.
more(默认值): 上述四项都不走MR.
minimal: 全表扫描, 列扫描, 分页查询不走MR.
none: 所有HiveSQL都走MR.
如果HiveSQL非要转MR程序, 能在本地执行, 就尽量不要交给Yarn来调度, 因为可能会涉及到跨域(跨机器)传输, 降低效率.
join优化有3种情况, 即:
小表join大表:
1. 开启Map端join, 在Map端(内存中)对数据做合并, 降低Reduce端拉取的数据量, 一方面提高传输效率, 另一方面可以防止出现数据倾斜.
2. 其实join优化你开与不开, 本质没有太大的区别, 因为Hive为了提高查询效率, 已经设置了, 自动join优化.
大表join大表:
假设有A表(100W条数据, id列50W是空), B表(60W条数据, id列40W空), 如果此时两张表根据 id列做关联查询, 会有大量的空值.
原始写法:
select * from A inner join B on A.id = B.id; -- 弊端是: id列有大量的空值, 无意义的操作较多.
1. 空值过滤.
select * from A inner join (select * from B where id is not null) B on A.id = B.id; -- 弊端: 会过滤掉大量的数据, 可能也会把有效的数据过滤掉
2. 空值转换.
# 2.1 不随机分布, 固定值.
select * from A inner join (
select 列1, 列2.., case when id is null then 10 end as id from B
) B on A.id = B.id; -- 虽然能解决, 但是null值过多, 会导致10过多, 将来出现数据倾斜的概率较大.
# 2.2 随机分布, 可变值..
select * from A inner join (
select 列1, 列2.., case when id is null then concat(值, rand()) end as id from B
) B on A.id = B.id;
分桶表 join 分桶表:
前提:
1. 两张表都是分桶表.
2. 某表的分桶数量 是另外表 分桶数量的 偶数倍.
结论:
表join连接查询的时候, 可以用 分桶字段替代 关联字段, 即:
原始SQL:
select * from A join B on A.id = B.id;
优化后SQL:
select * from A join B on A.分桶字段 = B.分桶字段;
map Join的主要思想就是,当关联的两个表是一个小表和一个大表的时候,我们把比较小的表直接放到内存中去,然后再对比较大的表进行map操作,join就发生在map操作的时候,每当扫描大表中的一行数据,就要去查看小表的数据,哪条与之相符,继而进行连接。
这样的join并不会涉及reduce操作,自然没有shuffle,减少了数据通过网络传输造成的高成本和高延迟,因为Join 是在 map 端完成的,所以又叫做map join.
在map阶段,map函数同时读取两个文件File1和File2,为了区分两种来源的key/value数据对,对每条数据打一个标签(tag)接下来通过shuffle 操作,就保证了相同key 的数据落在了同一个 reducer 中,然后在这个 reducer 中完成相应的 join 逻辑.
桶可以提高join 的效率,桶可以保证相同key 的数据都分在了一个桶里,这个时候我们关联的时候不需要去扫描整个表的数据,只需要扫描对应桶里的数据(因为key 相同的一定在一个桶里),smb的设计是为了解决大表和大表之间的join的,核心思想就是大表化成小表,然后map side join 解决是典型的分而治之的思想。
这里有一点要注意,那就是数据落在那个桶里不止和key 的值相关,还和桶的个数相关,因为我们是根据 key 的哈希值然后对桶的个数取余数获得一个值,然后根据这个值将数据放到对应的桶里去的,所以一般情况下我们要求不止是两个分桶表的分桶字段是相等的,还要求桶的个数是倍数关系(相等也是可以的)
-- map Join
set hive.auto.convert.join.noconditionaltask.size=512000000 ;
set hive.auto.convert.join=true;
-- bucket map join
1) 开启bucket map join功能: set hive.optimize.bucketmapjoin = true;
2) 一个表的bucket数是另一个表bucket数的整数倍
3) bucket列 == join列
4) 必须是应用在map join的场景中
-- smb
1) 保证join的表必须是桶表:
set hive.enforce.bucketing=true; --写入数据强制分桶
2) 在建表的时候, 必须设置分桶排序字段 而且需要保证 分桶字段 = join的字段 = 排序的字段
create table test_smb_2(mid string,age_id string)
CLUSTERED BY(mid) SORTED BY(mid) INTO 500 BUCKETS;
set hive.enforce.sorting=true; -- 开启强制排序
3) 两个分桶表的分桶的数量必须一致
4) 必须建立bucket map join的基础上
set hive.optimize.bucketmapjoin = true;
5) 必须开启 SMB join
set hive.auto.convert.sortmerge.join=true;
set hive.auto.convert.sortmerge.join.noconditionaltask=true;
6) 必须开启 自动尝试使用SMB
set hive.optimize.bucketmapjoin.sortedmerge = true;
能写 select 列1, 列2 就不要写 select *
查询时, 如果是分区表, 记得写 where 分组字段
如果数据量大, 容易出错, 可以改成 先分组, 后统计, 虽然会转2个MR, 执行速度慢了, 但是数据量大的情况下, 也可以成功执行.
笛卡尔积一般无意义, 且数据join次数多, 因此要避免.
描述: 分配给ReduceTask端的数据不均衡导致的问题. 例如: 1个ReduceTask处理100W条数据, 另1个ReduceTask处理100条数据.
解决方案:
手动开启负载均衡, 程序的底层会转两个MR程序来执行该任务, 第1个MR程序负责把倾斜的数据随机打散, 交给不同的ReduceTask来处理.
第1个MR程序的(Reduce端结果) 作为 第2个MR程序的Map段数据源, 然后由第2个MR的Reduce负责合并数据.
手动调大动态分区数, 默认动态分区上限是1000, 如果HQL分区数量超过它, 会报错, 我们调大分区数即可.
小细节: 动态分区的时候, 可以关闭严格模式, 因为严格模式要求: 动态分区时至少有1个静态分区.
默认情况下, HiveSQL只会执行1个阶段, 如果多阶段之间依赖度不高, 我们可以开启并行执行机制.
并行执行机制, 默认并行度是 8, 我们可以调大一些.
这个严格模式指的是 禁用低效的SQL, 即: 如果SQL比较低效, 压根儿不让你执行.
低效sql例如:
Hive2.X已开启, 无需设置, Hive2.X以前Container容器用一次就释放了, 开启JVM重用, 可以重复利用这些Container资源容器.
在HQL前加 explain执行计划, 查看SQL的执行分几个阶段, 阶段越少, 执行速度越快.