数据采样
分桶表概述:
分桶就是分文件, 在创建表的时候, 指定分桶字段, 并设置分多少个桶, 在添加数据的时候, hive会根据设置分桶字段, 将数据划分到N个桶(文件)中, 默认情况采用HASH分桶方案 , 分多少个桶, 取决于建表的时候, 设置分桶数量, 分了多少个桶最终翻译的MR也就会运行多少个reduce程序(HIVE的分桶本质上就是MR的分区操作)
如何向桶表添加数据
创建一个分桶表
sql
create table 表名(
字段 类型,
....
)
clustered by(分桶字段) [sorted by (字段 [asc | desc])] into N buckets
-- 定义分桶表核心语句
row format......
添加数据
sql
insert into|overwrite + select 将数据添加到桶表
bash
现有一个文本文件数据, 需要加载到分桶表,如何解决呢?
第一步: 基于桶表创建一张临时表, 此表和桶表保持相同字段, 唯一区别, 当前这个表不是一个桶表
第二步: 将数据先加载到这个临时表中
第三步: 基于临时表, 使用 insert into|overwrite + select 将数据添加到桶表
注:在CDH中默认开启了一个参数, 是禁止采用load data方式向桶表添加数据的: set hive.strict.checks.bucketing = true;
桶表的作用
1.进行数据采样工作
当表的数据量比较庞大的时候, 在编写SQL语句后, 需要首先测试 SQL是否可以正常的执行, 需要在表中执行查询操作, 由于表数据量比较庞大, 在测试一条SQL的时候整个运行的时间比较久, 为了提升测试效率, 可以整个表抽样出一部分的数据, 进行测试
校验数据的可行性(质量校验)
进行统计分析的时候, 并不需要统计出具体的指标, 可能统计的都是一些相对性指标, 比如说一些比率(合格率)问题, 此时可以通过采样处理
2.提升查询的效率
可以减少JOIN次数, 从而提升效率
如何采样
bash
采样函数: tablesample(bucket x out of y [on column])
使用位置:紧紧跟在表名的后面, 如果表名有别名, 必须放置别名的前面
说明:
x: 从第几个桶开始进行采样;
y: 抽样比例; column: 分桶的字段, 可以省略
注意:
1. x 不能大于 y;
2. y 必须是表的分桶数量的倍数或者因子
案例:
1) 假设 A表有10个桶, 请分析, 下面的采样函数, 会将那些桶抽取出来呢?
tablesample(bucket 2 out of 5 on xxx)
共计抽取2个桶, 分别是 1号桶和6号桶
2) 假设 A 表有20个桶, 请分析, 下面的抽样函数, 会将那些桶抽取出来呢?
tablesample(bucket 4 out of 4 on xxx)
共计抽取5个桶, 分别是 3,7,11,15,19
tablesample(bucket 8 out of 40 on xxx)
抽取二分之一个桶, 分别是7号桶二分之一
join 优化
Map Join
Map Join: 每一个mapTask在读取数据的时候, 每读取一条数据, 就会和内存中班级表数据进行匹配, 如果能匹配的上, 将匹配上数据合并在一起, 输出即可
好处: 将原有reduce join 问题全部都可以解决
弊端:
-
比较消耗内存
-
要求整个 Join 中, 必须的都有一个小表, 否则无法放入到内存中
仅适用于: 小表 join 大表 | 大表 join 小表
配置:
sql
set hive.auto.convert.join=true;
-- 开启 map join的支持 默认值为True
set hive.auto.convert.join.noconditionaltask.size=20971520;
-- 设置 小表数据量的最大阈值: 默认值为 20971520(20M)
Bucket Map Join
适用场景: 中型表 和 大表 join:
使用条件:
1- Join两个表必须是分桶表
2- 开启 Bucket Map Join 支持: set hive.optimize.bucketmapjoin = true;
3- 一个表的分桶数量是另一个表的分桶数量的整倍数
4- 分桶列 必须 是 join的ON条件的列
5- 必须建立在Map Join场景中(中型表是小表的3倍, 此时分至少3个桶)
SMB Join
适用场景: 大表 和 大表 join
使用条件:
1- 两个表必须都是分桶表
2- 开启 SMB Join 支持:
sql
set hive.auto.convert.sortmerge.join=true;
set hive.optimize.bucketmapjoin.sortedmerge = true;
set hive.auto.convert.sortmerge.join.noconditionaltask=true;
3- 两个表的分桶的数量是一致的
4- 分桶列 必须是 join的 on条件的列, 同时必须保证按照分桶列进行排序操作
sql
-- 开启强制排序
set hive.enforce.sorting=true;
-- 在建分桶表使用: 必须使用sorted by()
5- 应用在Bucket Map Join 场景中
sql
-- 开启 bucket map join
set hive.optimize.bucketmapjoin = true;
6- 必须开启HIVE自动尝试使用SMB 方案:
sql
set hive.optimize.bucketmapjoin.sortedmerge = true;
HIVE的索引
Row Group Index索引
bash
条件:
1) 要求表的存储类型为ORC存储格式
2) 在创建表的时候, 必须开启 row group index 索引支持
'orc.create.index'='true'
3) 在插入数据的时候, 必须保证需求进行索引列, 按序插入数据
适用于: 数值类型的, 并且对数值类型进行 > < = 操作
思路:
插入数据到ORC表后, 会自动进行划分为多个script片段, 每个片段内部, 会保存着每个字段的最小, 最大值, 这样, 当执行查询 > < = 的条件筛选操作的时候, 根据最小最大值锁定相关的script片段, 从而减少数据扫描量, 提升效率
操作:
CREATE TABLE lxw1234_orc2 (字段列表 ....) stored AS ORC
TBLPROPERTIES (
'orc.compress'='SNAPPY',
-- 开启行组索引
'orc.create.index'='true'
)
插入数据的时候, 需要保证数据有序的
insert overwrite table lxw1234_orc2
SELECT id, pcid FROM lxw1234_text
-- 插入的数据保持排序(可以使用全局排序, 也可以使用局部排序, 只需要保证一定有序即可, 建议使用局部排序 插入数据效率高一些, 因为全局排序只有一个reduce)
DISTRIBUTE BY id sort BY id;
使用:
set hive.optimize.index.filter=true;
SELECT COUNT(1) FROM lxw1234_orc1 WHERE id >= 1382 AND id <= 1399;
Bloom Fliter Index索引
bash
条件:
1) 要求表的存储类型为 ORC存储方案
2) 在建表的饿时候, 必须设置为那些列构建布隆索引
3) 仅能适合于等值过滤查询操作
思路:
在开启布隆过滤索引后, 可以针对某个列, 或者某几列来建立索引, 构建索引后, 会将这一列的数据的值存储在对应script片段的索引信息中, 这样当进行 等值查询的时候, 首先会到每一个script片段的索引中, 判断是否有这个值, 如果没有, 直接跳过script, 从而减少数据扫描量, 提升效率
操作:
CREATE TABLE lxw1234_orc2 (字段列表....)
stored AS ORC
TBLPROPERTIES (
'orc.compress'='SNAPPY',
-- 开启 行组索引 (可选的, 支持全部都打开, 也可以仅开启一个)
'orc.create.index'='true',
-- pcid字段开启BloomFilter索引
'orc.bloom.filter.columns'='pcid,字段2,字段3...'
)
插入数据: 没有要求, 当然如果开启行组索引, 可以将需要使用行组索引的字段, 进行有序插入即可
使用:
SET hive.optimize.index.filter=true;
SELECT COUNT(1) FROM lxw1234_orc1 WHERE id >= 0 AND id <= 1000 AND pcid IN ('0005E26F0DCCDB56F9041C','A');
数据倾斜
Join数据倾斜
解决方案一:
可以通过 Map Join Bucket Map Join 以及 SMB Join 解决,但是这种操作是存在使用条件的, 如果无法满足这些条件, 无法使用 这种处理方案
解决方案二:
将那些产生倾斜的key和对应v2的数据, 从当前这个MR中移出去, 单独找一个MR来处理即可, 处理后, 和之前的MR进行汇总结果即可,但是需要找到倾斜的key,两种解决方案: 运行期处理方案 和 编译期处理方案
运行期处理方案
sql
-- 实操:
set hive.optimize.skewjoin=true;
-- 开启运行期处理倾斜参数
set hive.skewjoin.key=100000;
-- 阈值, 此参数在实际生产环境中, 需要调整在一个合理的值(否则极易导致大量的key都是倾斜的)
-- 适用于: 并不清楚哪个key容易产生倾斜, 此时交由系统来动态检测
编译期处理方案
sql
-- 实操:
set hive.optimize.skewjoin.compiletime=true;
-- 开启编译期处理倾斜参数
CREATE TABLE list_bucket_single (key STRING, value STRING)
-- 倾斜的字段和需要拆分的key值
SKEWED BY (key) ON (1,5,6)
-- 为倾斜值创建子目录单独存放
[STORED AS DIRECTORIES]
-- 适用于: 提前知道那些key存在倾斜
union all 优化方案
不管是运行期 还是编译期的join倾斜解决, 最终都会运行多个MR, 将多个MR结果通过union all 进行汇总,union all也是需要单独一个MR来处理
解决方案:
让每一个MR在运行完成后, 直接将结果输出到目的地即可, 默认 是各个MR将结果输出临时目录, 通过 union all 合并到最终目的地
开启此参数即可:
sql
set hive.optimize.union.remove=true;
group by 数据倾斜
倾斜发生后, 出现的问题, 程序迟迟无法结束, 或者说翻译的MR中reduceTask有多个, 大部分的reduceTask都执行完成了, 只有其中一个或者几个没有执行完成, 此时认为发生了数据倾斜
方案一: 基于MR的 combiner(规约, 提前聚合) 减少数据达到reduce数量, 从而减轻倾斜问题
只需要在HIVE中开启combiner提前聚合配置参数即可:
sql
set hive.map.aggr=true;
方案二: 负载均衡的解决方案(需要运行两个MR来处理)
只需要开启负载均衡的HIVE参数配置即可:
sql
set hive.groupby.skewindata=true;