目录
- HIVE Skewed Table
- Skewed Join Optimization(最优化)
- Basic Partitioning
- List Bucketing
- Skewed Table vs List Bucketing Table
- List Bucketing Validation
HIVE Skewed Table
Skewed Table可用于提高一个或多个列具有偏斜值的表的性能。通过指定经常出现的值(严重偏斜),Hive会自动将它们拆分成单独的文件(或在列表存储的情况下为目录),并在查询过程中考虑到这一事实,以便它可以跳过或包括整个文件(或在列表存储的情况下为目录)。可以在表创建过程中在每个表级别上指定。
若是指定了STORED AS DIRECTORIES,也就是使用列表桶(ListBucketing),hive会对倾斜的值建立子目录,查询会更加得到优化。
下面的示例显示具有三个偏斜值的一列,还可以选择使用STORED AS DIRECTORIES子句来指定列表存储。
CREATE TABLE list_bucket_single (key STRING, value STRING) |
这是一个带有两个倾斜列的表的示例。
CREATE TABLE list_bucket_multiple (col1 STRING, col2 int, col3 STRING) |
可以使用alter table语句来对已创建的表修改倾斜信息。
ALTER TABLE table_name SKEWED BY (col_name1, col_name2, ...) ON ([(col_name1_value, col_name2_value, ...) [, (col_name1_value, col_name2_value), ...][STORED AS DIRECTORIES]; |
STORED AS DIRECTORIES
选项确定倾斜表是否使用列表存储功能,该功能为倾斜值创建子目录。
ALTER TABLE table_name NOT SKEWED; |
NOT SKEWED
选项使表不倾斜,并关闭列表存储功能(因为列表存储表始终是倾斜的)。这会影响在ALTER语句之后创建的分区,但对在ALTER语句之前创建的分区没有影响。
ALTER TABLE table_name NOT STORED AS DIRECTORIES; |
NOT STORED
会关闭列表存储功能,但是表仍然歪斜。
ALTER TABLE table_name SET SKEWED LOCATION (col_name1="location1" [, col_name2="location2", ...] ); |
修改list bucketing倾斜值的存储位置映射。
Skewed Join Optimization(最优化)
两个大数据表的连接由一组MapReduce作业完成,它们首先根据连接键对表进行排序,然后将它们连接起来,mapper将相同键的行发送到同一个reduce。
假设表A id字段有值1,2,3,4,并且表B也含有id列,含有值1,2,3。我们使用如下语句来进行连接。
select A.id from A join B on A.id = B.id |
将会有一组mappers读这两个表并基于连接键id发送到reducers,假设id=1的行分发到Reducer R1,id=2的分发到R2等等,这些reducer对A和B进行交叉连接,R4从A得到id=4的所有行,但是不会产生任何结果。
现在我们假定A在id=1上倾斜,这样R2和R3将会很快完成但是R1会执行很长时间,因此成为job的瓶颈。若是用户知道这些倾斜信息,这种瓶颈可以使用如下方法人工避免:
select A.id from A join B on A.id = B.id where A.id <> 1; |
第一个查询没有倾斜数据将会很快的完成,如果我们假定表B中只有少量的B.id=1的行,能够直接加载到内存中,通过将B的数据存储到内存中的哈希表中,join将会高效的完成,因此可以再mappper端进行连接,而不用reduce,效率会高很多,最后合并结果。
优点:
- 如果少量的倾斜键占了很大一部分数据,它们将不会成为瓶颈。
缺点:
表A和表B需要分别读和处理两次;
结果需要合并;
需要人工的处理倾斜数据。
hive为了避免上面的操作,在处理数据是对倾斜值进行特殊处理,首先读表B并且存储B.id=1的数据到内存的哈希表中,运行一组mappers来读取表A并做以下操作:
- 若id为1,使用B的id=1的哈希表来计算结果
- 对于其他值,发送到reducer端来join,这个reduce也会从B的mapper中得到对应需要连接的数据。
使用这种方法,最终我们只读取B两次,并且A中的倾斜数据在mapper中进行连接,不会被发送到reducer,其他的键值通过map/reduce。
假设B的行很少,而键在A中倾斜。因此可以将这些行加载到内存中。若是使用ListBucketing对倾斜值单独存储,会有更好的性能。在读倾斜的数据到内存中时可以指定到倾斜目录下的数据。
Basic Partitioning
有如下问题:存在许多表是这种格式
create table T(a, b, c, ....., x) partitioned by (ds); |
但是以下查询需要更加高效(这不扯蛋吗,有分区的查询条件不是分区):
select ... from T where x = 10; |
字段x中含有倾斜数据,一般情况下x的值中大约有1000个值有重度倾斜,其他值基数很小,当然,每天倾斜的X的值可能改变,上述要求可以通过以下方式解决:
为值“ x”创建一个分区。
create table T(a,b,c, .......) partitioned by (ds, x) |
优点
- 现有的Hive就足够了。
缺点
- HDFS可伸缩性:HDFS中的文件数量增加。
- HDFS可伸缩性:HDFS中的中间文件数量增加。例如,如果有1000个映射器和1000个分区,并且每个映射器每个键至少获得1行,我们最终将创建100万个中间文件。
- Metastore的可伸缩性:Metastore会随着分区数量的增长而扩展。
List Bucketing
上边方法提到将含有倾斜值得列作为分区存储,但是可能产生大量的目录,为什么不把列值不倾斜的放在一起呢,将每个倾斜的值单独存放一个目录,于是有了List Bucketing。
这个映射在表或分区级别的Metastore中维护。倾斜键的列表存储在表级别中,这个列表可以由客户端周期的提供,并且在新的分区载入时可以被更新。
如下例子,一个表含有一个x字段倾斜值的列表:6,20,30,40。当一个新的分区载入时,它会创建5个目录(4个目录对应x的4个倾斜值,另外一个目录是其余值)。这个表的数据被分成了5个部分:6,20,30,40,others。这跟上一节介绍的分桶表类似,桶的个数决定了文件的个数。倾斜键的整个列表存储在每个表或者分区中。
当使用一下查询时,hive编译器会仅仅使用x=30对应的目录去运行map-reduce。
select ... from T where ds = '2012-04-15' and x = 30; |
若是查询是x=50,则会使用x=others对应的目录去运行map-reduce作业。
select ... from T where ds = '2012-04-15' and x = 50; |
这种方法在一下条件下是很有效的:
- 每个分区的倾斜键占总数据的一大部分。在上边的例子中,如果倾斜的键(6,20,30,40)只占一小部分数据(比如20%),那么在查询x=50时依然需要扫描80%的数据。
- 每个分区的倾斜键数量非常少,因为这个倾斜值列表存在在元数据库中,在元数据库中为每个分区存储100w个倾斜键是没有意义的。
这种方法也可被扩展到含有多个列产生的倾斜键,例如我们想优化一下查询
select ... from T where x = 10 and y = 'b'; |
扩展以上的方法,对于(x,y)每个倾斜值,也按照上边方式单独存储,因此元数据库会有以下映射:
(10, 'a') --> 1, (10, 'b') --> 2, (20, 'c') --> 3, (others) --> 4. |
因此可直接找到2对应的目录,减少要处理的数据。
同时以下查询也会有一定程度的优化:
select ... from T where x = 10; |
以上两个语句在执行的过程中会裁剪掉一部分数据,例如,对x=10的查询hive编译器可以裁剪掉 ( 20 , c ) 对应的文件,对于 y = ‘b’,( 10 , ‘a’ ) 和 ( 20 , ‘c’ ) 对应的文件会被裁剪掉,一定程度能够减少扫描的数据量。
这种方法不适用于以下场景:
- 倾斜键数目非常多,元数据规模问题
- 许多情况下,倾斜键由多个列组成,但是在查询中,没有使用到倾斜键中的那些列。
Skewed Table vs List Bucketing Table
- Skewed Table是一个表它含有倾斜的信息。
- List Bucketing Table是Skewed Table,此外,它告诉hive使用列表桶的特点:为倾斜值创建子目录。
以下说明两者的存储区别:
create table t1 (x string) skewed by (x) on (‘a’, ‘b’) partitioned by dt location ‘/user/hive/warehouse/t1’; |
两者存储的形式如下所示:
/user/hive/warehouse/t1/dt=something/data.txt |
List Bucketing Validation
由于列表桶的子目录特点,它不能够与一些特征共存。
DDL
列表桶与以下共存会抛出编译错误:
- normal bucketing (clustered by, tablesample, etc.)
- external table
- “ load data …”
- CTAS (Create Table As Select) queries
DML
与一下DML操作共存也会跳出错误:
- “ insert into ”
- normal bucketing (clustered by, tablesample, etc.)
- external table
- non-RCfile due to merge
- non-partitioned table
参考文献:
1.HIVE ListBucketing
2.HIVE LanguageManualDDL-SkewedTables
3.HIVE Skewed Join Optimization