《Hive性能优化实战》读书笔记

发布时间 2023-10-13 17:25:13作者: 卤鸭架

写在前面

《Hive性能优化实战》是比较不错的一本hive技术书籍,介绍了hive相关的一些技术,一些基本的理论,看完能对hive优化方面略有了解; 但有俩地方每种不足,一是没有那么多的实际的综合情况分析优化案例,这个有点可惜,要是多几个案例就很不错了;而是执行计划部分大多数

整本书最吸引人的地方在第一章和第二章,实际测了下不同场景下的优化性能,并以新人和老人的角度分析了优化,刚读的时候很震撼。后续则是开始介绍hive相关组件、存储格式,计算引擎,yarn等等。

各章节总结回顾

第一章

各种因素对于sql性能的影响,直观展示了 MultiInsert语法改写,orc/parquet格式,源表文件大小,分区分桶这个方面对性能的影响

MultiInsert
-- union类型sql,改写为 MultiInsert
insert into table student_stat partition(tp)
select s_age,max(s_birth) stat,'max' tp
from student_tb_txt
group by s_age
union all
select s_age,min(s_birth) stat, 'min' tp
from student_tb_txt
group by s_age;


-- 改写后,这种写法只会读一次 student_tb_txt表,节省一个map阶段
from student_tb_txt
INSERT into table student_stat partition(tp)
select s_age,min(s_birth) stat,'min' tp
group by s_age
insert into table student_stat partition(tp)
select s_age,max(s_birth) stat,'max’ tp
group by s_age;

源表文件大小

源表的数据文件大小会显著影响性能,比如相同数据内容(行数字段存储文件类型都一样)的两个表 T-BIG 和 T-SMALL,T-BIG的数据分布在3个文件中每个文件7G,T-SMALL分布在500个文件每个40M

那么运行时最终的 Mapper 任务数量由于 HiveCombineInputFormat 的存在差异不大,但是 T-SMALL表每个服务器的 Mapper 任务大概率可以在本机节点读取文件,而 T-BIG表的不同服务器的 Mapper只能去那3个文件及副本所在的服务器读取文件,中间多了很多网络IO消耗,因此性能较差

需要注意的是,如果源表文件太小,也可能产生 Mapper从不同服务器获取文件进行合并的场景,也会产生网络IO,因此建议源表文件控制在和 HDFS一个BLOCK的大小,通常是128M。

数据格式

纯HIVE场景无脑选ORC就完事了,有大数据场景可以考虑Parquet。都是行列式的存储格式,即把10000行数据分一个组,这个组内按列来存储数据,并且 header和footer存了schema以及统计信息,还会压缩,性能和存储比 text不知道高到哪里去了

分区分桶

分区用的多一些,相当于把数据存到不同的目录,查询时指定分区键就会去指定目录查数,提升性能,通常不会用业务键来做分区,一般都是日分区、月分区、年分区等。

分桶是在单个分区目录下(也可不分区)将数据按照某个字段hash取模然后存入不同的文件内,及把数据按照某个键分组存到不同文件,这样查数时,过滤分桶键的时候就可以立马确定要查哪几个文件,又是一个性能提升。 不过生产上用的比较少,某些地方就不让用,因为分桶数量在建表时确定且后续不能更改,所以业务发生变化后,原先的设计反而可能影响性能。

第二章

第二章主要是介绍了下调优的思路,以及一些开发规范。 很有意思的是从小白的视角和老兵的视角来看待调优。感觉自己还是个小白视角hah

老兵视角的过度优化的案例很有意思,看的时候把我给震惊住了。如下sql是经典的优化count(distinct )数据倾斜的案例。

  • 小白认为当数据量特别大的时候,通过group by可以将数据分布到多个reducer处理,避免倾斜; 这确实没错,大部分情况都是OK的

  • 老兵认为, 需要计算的字段是 age年龄,这个字段的特殊性决定了最多就 120个枚举值,通过 map端聚合,就算有1000个map,每个map100个年龄,int类型字段4个字节最终也就是400KB的大小,最终输出的数据量汇总到单个reducer的时候也是可以吃的消的; 并且就算真的数据量大还可以开 hive3.0提供的 hive.optimize.countdistinct 参数,将hashtable改成 bitmap来提速; 且count(distinct age) 可读性好

-- 小白sql
select count(1) from(
  select s_age
  from student_tb_orc
  group by s_age
) b

-- 老兵sql
select count(distinct s_age)
from student_tb_orc

总结下调优的过程:

  1. 确定好性能目标,评估整个链路耗时,有性能瓶颈时才做优化,否则是浪费时间。
  2. 分析性能瓶颈,通常是出现在IO较多的地方
  3. 进行性能监控和告警

第三章

hive的安装相关

第四章

介绍了hive的基本组件和架构, 对执行原理有个概览

Hive自身组件
  • Hive Client: bin/目录下的 hive客户端,以及jdbc/thrift/beeline等
  • HiveServer2: 提供Web服务,接受各种类型客户端的访问,并处理sql解析,mr任务编译的工作
  • Metastore: 这位更是重量级,hive的灵魂,将 HDFS 文件系统转化为一个数据库的核心部分
Hive相关组件
  • hdfs: hive本身不存数据,数据都存在hdfs上面
  • mr: hive本身也没有计算框架,是将sql改写为mr任务,使用hadoop提供的mr计算框架来算数
  • yarn: 资源管理框架
  • tez: 计算框架
  • spark: 计算框架

第五章

介绍 mr以及mr过程中,hive可以控制的一些参数,参数最后面一起总结吧。下面这个基础的 wordcount 任务有个印象就行,主要的核心在shuffle上面,其实 shuffle的具体内容,知道个大概就行了,卷到啥 环形缓冲区,内存里的kv存的是啥就没意思了

import java.io.IOException;
import java.util.*;

import org.apache.hadoop.fs.Path;
import org.apache.hadoop.conf.*;
import org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapreduce.*;
import org.apache.hadoop.mapreduce.lib.input.*;
import org.apache.hadoop.mapreduce.lib.output.*;

public class WordCount {

    public static class MyMap extends Mapper<Object, Text, Text, IntWritable> {
        private final static IntWritable one = new IntWritable(1);
        private Text word = new Text();

        @Override
        protected void map(Object key, Text value, Mapper<Object, Text, Text, IntWritable>.Context context) throws IOException, InterruptedException {
//            System.out.println(key);
            String line = value.toString();
            StringTokenizer tokenizer = new StringTokenizer(line);
            while (tokenizer.hasMoreTokens()) {
                word.set(tokenizer.nextToken());
                context.write(word, one);
            }
        }
    }

    public static class Reduce extends Reducer<Text, IntWritable, Text, IntWritable> {
        @Override
        protected void reduce(Text key, Iterable<IntWritable> values, Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws IOException, InterruptedException {
            int sum = 0;
            while (values.iterator().hasNext()) {
                sum += values.iterator().next().get();
            }
            context.write(key, new IntWritable(sum));
        }
    }

    public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();
        conf.set("mapreduce.input.fileinputformat.split.minsize", "4194304");
        conf.set("mapreduce.input.fileinputformat.split.maxsize", "4194304");
        conf.set("mapreduce.input.fileinputformat.split.minsize.per.node", "4194304");
        conf.set("mapreduce.input.fileinputformat.split.minsize.per.rack", "4194304");


        Job job = Job.getInstance(conf, "wordcount");
        job.setMapperClass(MyMap.class);
        job.setReducerClass(Reduce.class);


        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(IntWritable.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);

        job.setInputFormatClass(CombineTextInputFormat.class);
        job.setOutputFormatClass(TextOutputFormat.class);

        CombineTextInputFormat.addInputPath(job, new Path(args[0]));
        TextOutputFormat.setOutputPath(job, new Path(args[1]));

        System.exit(job.waitForCompletion(true) ? 0 : 1);
    }
}
第六章

执行计划解读,mr的执行计划,没有tez的可惜,不过差不多

第七章

一些基本的sql处理模式,如where的过滤,group by的聚合,join的关联

第八、九、十、十一章

介绍了下 yarn怎么看, orc的结构特点,metastore的监控脚本,hive体系总结