第6章-Hive技巧与核心复盘
一个SQL语句分析
1 | SELECT a.Key |
1 | 解释:这个SQL其实是一个解决数据倾斜的SQL |
Hive的Web工具-HUE
1 | Hive本身提供了Hwi web界面,但使用起来不方便 |

【扩展内容】Hive数据倾斜的解决方案
1 | 可能会触发Hive数据倾斜的几种情况 |
1 | 原因: |
1 | 表现: |
1 常见数据压缩格式的使用
1 | 本次Hive扩展内容部分主要讲解Hive的数据存储格式,因为咱们前面默认使用的都是TextFile格式的数据,这种格式的数据在存储层面占用的空间比较大,影响存储能力,也影响计算效率。 |
数据压缩格式
MapReduce中常见的数据压缩格式
1 | MapReduce中常见的数据压缩格式主要包括下面这些: |
1 | 我们可以在Hadoop节点上执行hadoop checknative命令来确认一下。 |
1 | 这里面主要有zlib、snappy、lz4、bzip2。 |
1 | DEFLATE |
1 | 在这里我们主要分析一下这几种压缩格式的自身特点和性能 |

1 | 主要针对压缩格式的文件扩展名、是否可切分、压缩比、压缩速度和解压速度这几项进行对比。 |
1 | 针对表格中的这几种压缩格式,他们的文件扩展名还是很容易区分的,基本上都是以压缩格式名作为后缀,这个主要是为了后期能够通过文件后缀名快速区分出来使用的哪种压缩格式。 |
1 | 我们平时在使用windows中的压缩工具的时候其实也可以选择使用速度最快或者体积最小,大家在下面可以试验一下,如果选择体积最小,肯定速度就比较慢了。 |

数据压缩格式选择建议
1 | 结合前面分析的数据压缩格式的特点进行考虑,Bzip2这个压缩格式虽然支持切分,并且压缩效果也是最好的,但是并不一定在所有场景下都是最优的选择,因为它的压缩和解压是最慢的。 |
压缩位置
1 | 在MapReduce的整个过程中,可以在两个地方设置数据压缩格式: |

1 | 针对Map阶段的输出数据:建议选择压缩和解压速度快的压缩格式。Map阶段的数据落盘后会通过Shuffle,也就是通过网络传输到Reduce端。压缩Map的输出是可以提高网络传输效率的。但是压缩Map的输出会增加CPU的消耗。Map阶段在处理数据的时候自己本来就会消耗过多的CPU,所以此时应该重点考虑使用压缩和解压速度比较快的LZO、Snappy。 |
1 | 针对Reduce阶段的输出数据:需要分为两种场景。 |
数据压缩格式案例实战

1 | 前面我们对常见的数据压缩格式有了一定的了解,下面我们通过一个案例具体演示一下这些数据压缩格式的使用。 |
1 | 这个图里面显示的是未压缩的数据文件和各种压缩格式压缩后的数据文件的大小。 |
1 | 下面我们来看一下具体的压缩过程: |
1 | package com.imooc.compress; |
1 | 开发一个生成测试数据的代码:生成一个2G的文件。 |
1 | 将生成的2G文件上传HDFS上。 |
1 | 注意:在执行下面任务的时候,集群容易出现磁盘不够用的情况,可能会导致DataNode节点状态异常,进而导致集群不可用。 |

1 | 如果发现MapReduce任务提交到集群上却无法正常执行,一直处于等待状态,排查集群发现进程都在。 |

1 | 因为我们的虚拟机磁盘默认分配的是20G,空间不是很大,集群使用一段时间之后,HDFS中的数据就比较多了,磁盘就不够用了,找到HDFS中的一些大文件删除一下即可,并且把回收站中的内容也清理一下。 |
1 | [root@bigdata04 ~]# hdfs dfs -du -h / |
1 | 注意:在删除HDFS中大文件的时候最好带上skipTrash,这样删除的文件就不会进到回收站了,否则删完以后HDFS的空间依然不会释放。 |
执行未压缩的任务
1 | [root@bigdata04 hadoop-3.2.0]# hadoop jar db_hadoop-1.0-SNAPSHOT-jar-with-dependencies.jar com.imooc.compress.MrDataCompress /words.dat /words_out/non_compress |
执行Deflate压缩格式的任务
1 | 此时需要用到Deflate压缩格式实现类的全类名,其实这些压缩格式的实现类在MapReduce的源码中是可以找到的。 |
1 | public void initialize(InputSplit genericSplit, |
1 | 找到SplittableCompressionCodec接口,这个接口继承了CompressionCodec接口,查看CompressionCodec接口的实现类: |
1 | 其实通过代码也可以看到这些压缩格式是否可以支持切分。 |
1 | 产生的结果数据是这样的: |
1 | 其实前面通过代码层面可以验证Deflate压缩格式的数据文件不支持切分,不过最好还是能通过代码验证一下。 |
1 | 然后关注下面的日志信息,主要查看number of splits后面的值,这个值表示针对任务的输入数据会产生几个Split。 |
1 | 这里显示的是1,说明 /words_out/compress_deflate/part-r-00000.deflate这个346M的文件只会产生1个Split,最终也只会产生1个map任务。 |
1 | 为了减少HDFS空间占用,查看后建议删除生成的压缩文件。 |
执行Gzip压缩格式的任务
1 | 针对Reduce输出结果进行压缩。 |
1 | 产生的结果数据是这样的: |
1 | 为了减少HDFS空间占用,查看后建议删除生成的压缩文件。 |
1 | 验证Gzip压缩格式的数据是否支持切分。 |
执行Bzip2压缩格式的任务
1 | 针对Reduce输出结果进行压缩。 |
1 | 验证Bzip2压缩格式的数据是否支持切分。 |
执行Lz4压缩格式的任务
1 | 针对Reduce输出结果进行压缩。 |
1 | 验证Lz4压缩格式的数据是否支持切分。 |
执行Snappy压缩格式的任务
1 | 针对Reduce输出结果进行压缩。 |
1 | 验证Snappy压缩格式的数据是否支持切分。 |
执行Lzo压缩格式的任务
1 | 注意:需要先在Hadoop集群中集成Lzo。 |
1 | 然后同步到bigdata02和bigdata03中。 |
1 | (2)使用Lzo压缩。 |
1 | 验证Lzo压缩格式的数据是否支持切分。 |
1 | 针对Lzo文件,想要实现可切分,需要对Lzo文件创建索引。 |
1 | 重新验证一下Lzo文件的切分效果。 |
2 常见数据存储格式的使用
1 | 在最开始学习Hive的时候我们说到了,Hive没有专门的数据存储格式,默认可以直接加载文本文件TextFile,还支持SequenceFile、RCFile这些。 |

1 | 其中RCFile数据存储格式是从Hive 0.6版本开始支持的。 |


1 | 目前工作中使用最多的是TextFile、ORC和Parquet。 |
数据存储格式之TextFile
1 | TextFile是Hive的默认数据存储格式,基于行存储。 |
1 | create external table stu_textfile( |
1 | 然后生成测试数据,并把测试数据加载到这个表中。 |
1 | 将这个数据文件上传到Hive表stu_textfile对应的hdfs目录中。 |
使用Deflate压缩格式
1 | 接下来基于stu_textfile这个普通数据表构建一个新的压缩数据表。 |
1 | 接下来通过insert into select从普通表中查询数据,插入到压缩表中。 |
1 | 接下来写一个sql查询压缩表中的数据,确认一下是否会生成多个Map任务。 |
1 | 在这里发现这个SQL最终只生成了1个Map任务,也就意味着这个388M的文件是有一个Map任务负责计算。 |
使用Bzip2压缩格式
1 | 再来验证一下Bzip2压缩格式。 |
1 | 指定bzip2压缩格式: |
1 | 接下来写一个sql查询压缩表中的数据,确认一下是否会生成多个Map任务。 |
1 | 结果发现只产生了1个Map任务,正常情况下这份数据会被切分成2个Split,产生2个Map任务的,为什么这里只有1个Map任务呢? |
1 | 这个时候发现产生了2个Map任务,最终可以确认,在Hive中Bzip2压缩文件也是支持切分的。 |
数据存储格式之SequenceFile
1 | 下面我们来看一下SequenceFile这种存储格式 |
1 | 由于Record是针对每一条数据分别进行压缩,压缩率比较低,所以一般都会选择Block压缩。 |
不使用压缩
1 | 先不使用压缩,创建一个SequenceFile存储格式的表。 |
1 | 确认一下这个表: |
1 | 如果INPUTFORMAT使用的是SequenceFileInputFormat,说明这个表中需要存储SequenceFile格式的数据。 |
1 | 验证是否支持切分: |
1 | package com.imooc.compress; |
1 | 执行代码,可以看到这样的结果: |
1 | 这个表中的数据验证完毕之后,建议删除一下数据,释放HDFS空间。 |
使用Deflate压缩(Block级别)
1 | 接下来使用Deflate压缩试验一下,基于Block压缩级别 |
1 | 指定使用deflate压缩,Block级别。 |
1 | 验证是否支持切分: |
1 | 这个怎么理解呢? |

1 | 上面表示是SequenceFile中的数据,每一个Record代表里面的一条数据。 |

1 | 当使用Block级别压缩的时候,SequenceFile中的数据是以Block为单位存储的,每个Block中存储多个Record,并且对Block内部的多个Record统一压缩存储。 |
数据存储格式之RCFile
1 | 接下来我们来看一下RCFile这个数据存储格式。 |

1 | 左边的Relation表示关系型数据库中的数据存储形式,可以把它认为是一张表,表中有A\B\C\D这几个列。 |
1 | 下面我们来演示一下具体的用法。 |
不使用压缩
1 | 先不使用压缩,创建一个RCFile存储格式的表。 |
1 | 然后从普通表中查询数据导入到这个RCFile存储格式的表中。 |
1 | 验证是否支持切分: |
1 | 可以看到产生了7个map任务,说明RCFile格式的文件是支持切分的。 |
使用Deflate压缩
1 | 接下来使用Deflate压缩格式试验一下。 |
1 | 验证是否支持切分: |
数据存储格式之ORC
1 | 下面我们来看一下ORC这个数据存储格式。 |

1 | 这个图来源于官网,大致可以看出来ORC中的数据首先会被划分为多个Stripe,每个Stripe 250M。Stripe表示ORC文件存储数据的地方。 |
1 | 这个图看起来稍微有点复杂,简化一下是这样的: |

1 | 从这个图里面可以看出来ORC存储格式和RCFile存储格式在存储形式上没有特别大的区别,核心思想还是行列式存储。 |
1 | 注意:tblproperties中可以指定多个参数,多个参数之间使用逗号分割即可。 |

1 | -orc.compress:表示ORC文件的压缩类型,支持NONE、ZLIB和SNAPPY,默认值是ZLIB。这里的ZLIB其实就和前面我们所说的Deflate压缩格式是类似的,因为Deflate压缩格式底层就是用的ZLIB。 |
1 | 注意:从官网文档资料上来看,ORC只支持NONE、ZLIB、SNAPPY这三种压缩格式。默认是ZLIB,到这可以确认一下。 |

1 | 其实目前ORC还支持其他压缩格式,这个等一会我们来验证一下。 |
不使用压缩
1 | 先不使用压缩,创建一个ORC存储格式的表。 |
1 | 确认一下这个表: |
1 | 然后从普通表中查询数据导入到这个RCFile存储格式的表中。 |
1 | 验证是否支持切分: |
使用Zlib压缩
1 | 接下来使用Zlib压缩格式试验一下。 |
1 | 验证是否支持切分: |
使用Snappy压缩
1 | 接下来使用Snappy压缩格式试验一下。 |
1 | 验证是否支持切分: |
验证是否支持其他压缩格式
1 | 首先验证一下是否支持bzip2 |
1 | 这个错误信息说的是不支持Bzip2,这个错误是在CompressionKind这个类中抛出来的,那也就意味着在这个类里面会对压缩格式进行判断。 |

1 | 然后选择hive-3.1.2这个分支的代码,因为我们现在使用的是这个版本。 |

1 | 在这个项目中搜索CompressionKind这个类即可。 |



1 | 所以说现在,ORC主要支持这四种压缩格式,这就说明Hive的文档不是最新的了。还是要以源码为主。 |
数据存储格式之PARQUET
1 | 下面我们来看一下parquet这个数据存储格式。 |

1 | 这个图是来源图官网的,看起来有点乱。 |

1 | Parquet存储格式比ORC更复杂一些,多封装出来一层Page。ORC存储格式是把数据内容直接存储到了Column这一层。 |

1 | 再找到里面的parquet-hadoop子项目 |

1 | 查看这个项目中的README.md文件中的内容 |

不使用压缩
1 | 先不使用压缩,创建一个Parquet存储格式的表。 |
1 | 验证是否支持切分: |
使用Gzip压缩
1 | 接下来使用Gzip压缩格式试验一下。 |
1 | 验证是否支持切分: |
使用Snappy压缩
1 | 接下来使用Snappy压缩格式试验一下。 |
数据存储格式总结
数据存储格式之存储空间汇总
1 | 前面我们分析了这么多种数据存储格式,具体在工作中应该如何选择呢? |

1 | 原始文本文件大小为:2.09G。 |
数据存储格式之压缩格式选择
1 | 在使用ORC数据存储格式的时候,最好再配合上合适的压缩格式,这样可以进一步挖掘ORC的存储性能。 |

1 | 从这里可以看出来,Snappy的压缩和解压速度相对更好一些。 |
1 | 假设我希望这一份数据,既可以在Hive中使用,还希望在Impala中使用,这个时候就需要重点考虑这个数据格式是否能满足多平台同时使用。 |