Hive性能调优(一)
本文章对Hive性能调优实战这本书的总结
第一章
数据准备
init_student 学生数据
# coding: utf-8
import random
import datetime
import sys
reload(sys)
sys.setdefaultencoding('utf8')
# lastname和first都是为了来随机构造名称
lastname = u"赵李周吴郑王冯陈褚卫蒋沈韩杨朱秦尤许何吕施张孔曹严华金魏陶姜戚谢邹喻柏水窦章云苏潘葛奚范彭郎鲁韦昌马苗"
firstname = u"红尘冷暖岁月清浅仓促间遗落一地如诗的句点不甘愿不决绝掬一份刻骨的思念系一根心的挂牵在你回眸抹兰轩的底色悄然"
#创建一个函数,参数start表示循环的批次
def create_student_dict(start):
firstlen = len(firstname)
lastlen = len(lastname)
# 创建一个符合正太分布的分数队列 100均值 50标准差
scoreList = [int(random.normalvariate(100, 50)) for _ in xrange(1, 5000)]
# 创建1万条记录,如果执行程序内存够大这个可以适当调大
filename = str(start) + '.txt'
print(filename)
#每次循环都创建一个文件,文件名为:循环次数+'.txt',例如 1.txt
with open('./' + filename, mode='wr+') as fp:
for i in xrange(start * 40000, (start + 1) * 40000):
firstind = random.randint(1, firstlen - 4)
model = {"s_no": u"xuehao_no_" + str(i),
"s_name": u"{0}{1}".format(lastname[random.randint(1, lastlen - 1)],
firstname[firstind: firstind + 1]),
"s_birth": u"{0}-{1}-{2}".format(random.randint(1991, 2000),
'0' + str(random.randint(1, 9)),
random.randint(10, 28)),
"s_age": random.sample([20, 20, 20, 20, 21, 22, 23, 24, 25, 26], 1)[0],
"s_sex": str(random.sample(['男', '女'], 1)[0]),
"s_score": abs(scoreList[random.randint(1000, 4990)]),
's_desc': u"为程序猿攻城狮队伍补充新鲜血液,"
u"为祖国未来科技产业贡献一份自己的力量" * random.randint(1, 20)}
#写入数据到本地文件
fp.write("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}".
format(model['s_no'], model['s_name'],
model['s_birth'], model['s_age'],
model['s_sex'], model['s_score'],
model['s_desc']))
# 循环创建记录,一共是40000*25=100万的数据
for i in xrange(1, 11):
starttime = datetime.datetime.now()
create_student_dict(i)
学科数据
# coding: utf-8
import random, datetime
import sys
reload(sys)
sys.setdefaultencoding('utf8')
#创建一个函数,参数start表示循环的批次
def create_student_sc_dict(start):
filename = str(start)+'.txt'
print start
with open('./'+filename , mode='wr+') as fp:
for i in xrange(start * 40000, (start + 1) * 40000):
#课程出现越多表示喜欢的人越多
course = random.sample([u'数学', u'数学', u'数学', u'数学', u'数学',
u'语文', u'英语', u'化学', u'物理', u'生物'], 1)[0]
model = {"s_no": u"xuehao_no_" + str(i),
"course": u"{0}".format(course),
"op_datetime": datetime.datetime.now().strftime("%Y-%m-%d"),
"reason": u"我非常非常非常非常非常非常非常"
u"非常非常非常非常非常非常非常喜爱{0}".format(course)}
line = "{0}\t{1}\t{2}\t{3}"\
.format(model['s_no'],
model['course'],
model['op_datetime'],
model['reason'])
fp.write(line)
# 循环创建记录,一共是40000*10=40万记录
for i in xrange(1, 11):
starttime = datetime.datetime.now() # create_student_dict 转换成dataframe格式,并注册临时表temp_student
create_student_sc_dict(i)
上传数据
hadoop fs -mkdir -p /bigdata/hive/warehouse/student_db_txt
hadoop fs -put ./*.txt /bigdata/hive/warehouse/student_db_txt
hadoop fs -mkdir -p /bigdata/hive/warehouse/student_sc_db_txt
hadoop fs -put ./*.txt /bigdata/hive/warehouse/student_sc_db_txt
创建Hive表
create table if not exists default.student_tb_txt(
s_no string comment '学号',
s_name string comment '姓名',
s_birth string comment '生日',
s_age bigint comment '年龄',
s_sex string comment '性别',
s_score bigint comment '综合能力得分',
s_desc string comment '自我介绍'
)
row format delimited fields terminated by '\t'
location '/bigdata/hive/warehouse/student_db_txt';
create table if not exists default.student_sc_tb_txt(
s_no string comment '学号',
course string comment '课程名',
op_datetime string comment '操作时间',
reason string comment '选课原因'
)
row format delimited fields terminated by '\t'
location '/bigdata/hive/warehouse/student_sc_db_txt';
@SQL语句对性能影响
使用UNION,查询student_tb_txt中,每个年龄段最晚出生和最早出生的人,存入student_stat
方案一
create table student_stat
(
a bigint,
b string
)partitioned by (tp string)
STORED AS TEXTFILE
-- 开启动态分区 和 本地模式
set hive.exec.dynamic.partition=true;
set hive.exec.dynamic.partition.mode=nostrict;
set hive.exec.mode.local.auto=true;
-- 找出各个年龄段 最早和最晚出生的信息 使用union all合并写入
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
-- 执行后的计算结果
Query ID = cjl_20210524184900_9d4dc8ad-7fc5-4621-af13-b69592ff2abe
Total jobs = 5
Launching Job 1 out of 5
Loaded : 2/2 partitions.
Time taken to load dynamic partitions: 0.319 seconds
Time taken for adding to write entity : 0.001 seconds
MapReduce Jobs Launched:
Stage-Stage-1: HDFS Read: 3700118556 HDFS Write: 2001580848 SUCCESS
Stage-Stage-9: HDFS Read: 3963117732 HDFS Write: 2001597674 SUCCESS
Stage-Stage-2: HDFS Read: 3963133909 HDFS Write: 2001605791 SUCCESS
Total MapReduce CPU Time Spent: 0 msec
OK
Time taken: 5.784 seconds
从上面的返回的结果来看一共有5个job对应3个MapReduce的任务,分别对应Stage-Stage-1、Stage-Stage-9、Stage-Stage-2
方案二 实现了MULTI-TABLE-INSERT
DROP TABLE IF EXISTS student_stat;
create table student_stat
(
a bigint,
b string
)partitioned by (tp string)
STORED AS TEXTFILE;
-- 找出各个年龄段 最早和最晚出生的信息 使用union all合并写入
from student_tb_txt
insert into table student_stat partition(tp)
select s_age,max(s_birth) stat, 'max' tp
group by s_age
insert into table student_stat partition(tp)
select s_age,min(s_birth) stat, 'min' tp
group by s_age
-- 执行结果
Query ID = cjl_20210524185514_5e88080a-9c5b-4ea5-afaa-96ce128ce947
Total jobs = 1
Launching Job 1 out of 1
MapReduce Jobs Launched:
Stage-Stage-2: HDFS Read: 4226124954 HDFS Write: 2001606124 SUCCESS
Total MapReduce CPU Time Spent: 0 msec
OK
Time taken: 4.765 seconds
数据块大小对性能影响
- 当student_tb_txt 用 500个文件 存储20GB文件
- student_tb_txt_bigfile 用2个文件存储相同的数据
- 处理student_tb_txt 时,启动了84个Map任务,
- 处理 student_tb_txt_bigfile时,启动了82个Map任务,一个文件要被分布在不同服务器的Map任务中读取,会更加慢一点
数据格式对性能影响
- SequenceFile 二进制key/value对的平面文件
- Parquet 列式数据存储,对多层嵌套的数据结构提供良好支持
- ORC 是对 RCFile的优化,更加高效 最快
@分区和分桶对性能影响
需要根据业务来
--如果存在student_orc_partition表就删除
DROP TABLE if EXISTS student_orc_partition_bucket;
--创建student_orc_partition_bucket,以part为分区列,s_age为分桶列
--part等于对s_no取hash值的结果取模10,即pmod(hash(s_no),10)
create table if not exists student_orc_partition_bucket(
s_no string ,
s_name string ,
s_birth string ,
s_age string,
s_sex string,
s_score bigint,
s_desc string
)
partitioned by(part bigint)
clustered BY (s_age) INTO 16 BUCKETS -- 分成16个桶
STORED AS ORC;
set hive.exec.dynamic.partition=true;
set hive.exec.dynamic.partition.mode=nonstrict;
set hive.enforce.bucketing = true; -- 开启强制分桶
-- part 等于s_no 取hash值的结果取模10
insert into table student_orc_partition_bucket partition(part)
select s_no,s_name,s_birth,s_age,s_sex,s_score,s_desc,pmod(hash(s_no),10) part
from student_tb_orc;
第一章 总结
HiveSQL在执行时会转化为各种计算引擎能够运算的算子,
- 首先要了解基本的MR过程和原理
- 尝试吧SQL拆解成计算引擎对应的算子,拆解完和explain对比
-- 查看表信息
desc formatted student_tb_txt
-- 找出各个年龄段 最早和最晚出生的信息 使用union all合并写入
from student_tb_txt
insert into table student_stat partition(tp)
select s_age,max(s_birth) stat, 'max' tp
group by s_age
insert into table student_stat partition(tp)
select s_age,min(s_birth) stat, 'min' tp
group by s_age
-- 分区分桶
partitioned by(part bigint)
clustered BY (s_age) INTO 16 BUCKETS -- 分成16个桶
STORED AS ORC;
-- 开启动态分区
set hive.exec.dynamic.partition=true;
set hive.exec.dynamic.partition.mode=nonstrict;
set hive.enforce.bucketing = true; -- 开启强制分桶
-- 本地执行
set hive.exec.mode.local.auto=true;
第二章
理解经典wordcount
- 阶段1: file 到Mapper阶段。在filel中将一行行的文本转化成键-值对的形式输入到Mapper中,其中键为行所在位置的偏移量(offset) ,值为文本内容/在Mapper的mp函数中输入的值,被切分成一个个单词并输出,输出也是以键_值对的形式输出的,其中,键是文本中的一个个单词,值是固定值1。
- 阶段2: Mapper 到Combiner阶段。Combiner 将汇集本地多个Mapper输出的结果并转化为键-值对的形式,输入键是Mapper 输出键,输入值是由Map输出键相同的输出值组成的一个数组。 例如,在node serverl中,mapperl 和mapper2各出现一次wordI单词,在combiner的输入表现形式键为wordl,值为数组[1,1],输出的值就会以2的形式输出。
- 阶段3:Combiner到Reducer阶段,所做的事和Combiner一样,
public class WordCount {
/*Mapper泛型接受声明了四个参数,
*前俩个Object, Text表示程序接受输入的数据类型,
*在MapReduce输入的数据都是采用键值对的形式,
*其中Object表示键,Text表示值,需要下面map函数的前俩个参数类型。
*后俩个Text, IntWritable表示map输出的键值对的数据类型
*map函数中context.write()函数接受的数据类型要和这俩个数据类型保持一致。
*/
public static class TokenizerMapper extends Mapper<Object, Text, Text, IntWritable>{
private final static IntWritable one = new IntWritable(1);
private Text word = new Text();
//Map执行逻辑代码。
//下面的代码接受三个参数
//参数- key表示偏移量,在HDFS的文件中每一行数据都有一个行偏移量
//参数-value表示每一行的内容
//参数-context表示MapReduce的上下文
public void map(Object key, Text value, Context context
) throws IOException, InterruptedException {
//将一行数据内容按空格切分成可以迭代的集合
StringTokenizer itr = new StringTokenizer(value.toString());
while (itr.hasMoreTokens()) {
//mapreduce不接受java的数据类型,必须转化成mapreduce可以识别的类型
//例如java中的String对应Text,int对应的IntWritable等等
word.set(itr.nextToken());
//context会将文件写到HDFS的临时目录中,等待Reducer节点来取
//写到HDFS的文件中,每一行表现形式如:word1 1
context.write(word, one);
}
}
}
/*Reducer泛型也接受声明了四个参数,
*前俩个,即Text和IntWritable分别表示读取Mapper输出键,值的数据类型
*和上面的Mapper泛型后面俩个参数的数据类型保持一致。
*后俩个,即Text和IntWritable分别表示Reducer输出键,值的数据类型
*reduce函数中context.write()函数接受的数据类型要和这俩个数据类型保持一致。
*/
public static class IntSumReducer extends Reducer<Text,IntWritable,Text,IntWritable> {
private IntWritable result = new IntWritable();
// Reduce阶段执行的逻辑代码
public void reduce(Text key, Iterable<IntWritable> values,
Context context
) throws IOException, InterruptedException {
int sum = 0;
for (IntWritable val : values) {
sum += val.get();
}
result.set(sum);
//context将结果写入到hdfs
//写到HDFS的文件中,每一行的表现形式如:word1 sum
//其中sum表示具体数值
context.write(key, result);
}
}
//下面是提交作业的逻辑
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
Job job = Job.getInstance(conf, "word count");
job.setJarByClass(WordCount.class);
job.setMapperClass(TokenizerMapper.class);
//Combiner实际执行的逻辑和reducer是一样的
//所以可以将Combiner的处理类直接用Reducer类来表示,
//和Reducer差别是,combiner是在Map阶段所在的节点执行
job.setCombinerClass(IntSumReducer.class);
job.setReducerClass(IntSumReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
//设置文件输入路径
FileInputFormat.addInputPath(job, new Path(args[0]));
//设置文件的输出路径
FileOutputFormat.setOutputPath(job, new Path(args[1]));
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
使用explain查看HiveSQL的执行
- 没有具体量化数据,只能知道执行逻辑
hive> explain select s_age,count(1) from student_tb_txt group by s_age;
OK
STAGE DEPENDENCIES:
Stage-1 is a root stage
Stage-0 depends on stages: Stage-1
STAGE PLANS:
Stage: Stage-1
Map Reduce
Map Operator Tree:
TableScan
alias: student_tb_txt
Statistics: Num rows: 54762456 Data size: 438099648 Basic stats: COMPLETE Column stats: NONE
Select Operator
expressions: s_age (type: bigint)
outputColumnNames: _col0
Statistics: Num rows: 54762456 Data size: 438099648 Basic stats: COMPLETE Column stats: NONE
Group By Operator
aggregations: count(1)
keys: _col0 (type: bigint)
mode: hash
outputColumnNames: _col0, _col1
Statistics: Num rows: 54762456 Data size: 438099648 Basic stats: COMPLETE Column stats: NONE
Reduce Output Operator
key expressions: _col0 (type: bigint)
sort order: +
Map-reduce partition columns: _col0 (type: bigint)
Statistics: Num rows: 54762456 Data size: 438099648 Basic stats: COMPLETE Column stats: NONE
value expressions: _col1 (type: bigint)
Reduce Operator Tree:
Group By Operator
aggregations: count(VALUE._col0)
keys: KEY._col0 (type: bigint)
mode: mergepartial
outputColumnNames: _col0, _col1
Statistics: Num rows: 27381228 Data size: 219049824 Basic stats: COMPLETE Column stats: NONE
File Output Operator
compressed: false
Statistics: Num rows: 27381228 Data size: 219049824 Basic stats: COMPLETE Column stats: NONE
table:
input format: org.apache.hadoop.mapred.SequenceFileInputFormat
output format: org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormat
serde: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe
Stage: Stage-0
Fetch Operator
limit: -1
Processor Tree:
ListSink
Time taken: 0.273 seconds, Fetched: 48 row(s)
使用YARN提供的日志查看执行
- 点击Tracking url
- 左边Job下的Counters链接
@通过改写SQL实现过程干预
--该案例演示的是使用版本新增的的命令关键词词写法,代替在早期的hive还不支持cube,rollup,--grouping sets等操作时,用union关键词来构建多维统计的方式
--改写前的代码段
select * from(
select s_age,s_sex,count(1) num
from student_tb_orc
group by s_age,s_sex
union all
select s_age,null s_sex,count(1) num
from student_tb_orc
group by s_age
) a
--改写后的代码段
select s_age,s_sex,count(1) num
from student_tb_orc
group by s_age,s_sex
grouping sets((s_age),(s_age,s_sex))
--该案例演绎的是用子查询代替count(distinct),避免因数据倾斜导致的性能问题
--改写前的代码段
--统计不同年龄段,考取的不同分数个数
select s_age,count(distinct s_score) num
from student_tb_orc
group by s_age
--改写后的代码段
select s_age,count(1) num
from(
select s_age,s_score,count(1) num
group by s_age,s_score
) a
@通过SQL-Hint方法
- hive把左表数据放在缓存中,右边表的数据做流数据表。
--MAPJOIN(),括号中指定的是数据量较小的表,表示在Map阶段完成a,b表的连接,
--将原来在Reduce中进行连接的操作,前推到Map阶段
SELECT /*+ MAPJOIN(b) */ a.key, a.value
FROM a JOIN b ON a.key = b.key
--STREAMTABLE(),括号中指定的数据量大的表。
--默认情况下在reduce阶段的进行连接,hive把左表数据放在缓存中,右边表的数据做流数据表。
--如果想改变上面的那种方式,就用/*+streamtable(表名)*/来指定你想要做为流数据的表
SELECT /*+ STREAMTABLE(a) */ a.val, b.val, c.val
FROM a
JOIN b ON (a.key = b.key1)
JOIN c ON (c.key = b.key1)
@通过开放的一些配置开关
--开启向量化查询开关
set hive.vectorized.execution.enabled=true;
--开启并行执行
set hive.exec.parallel=true;
set hive.exec.parallel.thread.number=2;
过度优化
- 方案二比方案一好
- 去重的是s_age列,枚举个数少
- distinct 会在内存中构建hashtable,group by有些会建,有些不会
- 在Hive3.0中增加了count(distinct)优化,配置hive.optimize.countdistinct
- 代码简介
方案一
select count(1) from(
select s_age
from student_tb_orc
group by s_age
) b
方案二
select count(distinct s_age)
from student_tb_orc
理透需求
不需要排序
Map阶段:
// context,mapreduce上下文
Mapper(key,value,context)
//将Map数据添加到set,对于存在相同的s_age会自动过滤,
//set的过滤方式时间复杂度O(1),
//请记住这里的去重只是局部去重,即在一个Map上做的,
//最终到Reduce节点可能存在重复
set.put(s_age)
//遍历set集合
foreach(item in set):
//用null作为key有俩个作用,1.减少后面操作的数据量,
//2.将所有年龄都打入到一个reduce,做全局去重
context.write(null,item)
Reduce阶段:
//将Map相同的key汇集到一个Reducer,不同value则保存在values数组中,
//由于Map输出的key为null,所有的数据都跑到一个reduce,且可以也为null
Reducer(key,values,context):
//对values进行去重
foreach(item in values):
set.put(item)
//求set集合的长度,即s_age的去重个数
count=set.length
context.write(count,null)
查看元信息
desc formatted student_tb_txt;
'''
OK
#这是表的描述信息
# col_name data_type comment
s_no string 学号
s_name string 姓名
s_birth string 生日
s_age bigint 年龄
s_sex string 性别
s_score bigint 综合能力分数
s_desc string 介绍信息
#表的详细描述信息
# Detailed Table Information
Database: default
#表的创建用户
Owner: hue
CreateTime: Thu Sep 27 17:53:05 CST 2018
LastAccessTime: UNKNOWN
Protect Mode: None
Retention: 0
#表在hdfs的位置
Location: hdfs://bigdata-02:8020/mnt/data/bigdata/warehouse/student_tb_txt
#表的类型,MANAGED表示内部表
Table Type: MANAGED_TABLE
#表的参数
Table Parameters:
COLUMN_STATS_ACCURATE true
numFiles 3
numRows 20000000
rawDataSize 54581459872
totalSize 54601459872
transient_lastDdlTime 1538041986
#表的存储信息
# Storage Information
# 表序列化和反序列化用的类
SerDe Library: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe
# 输入的数据格式
InputFormat: org.apache.hadoop.mapred.TextInputFormat
#输出的数据格式
OutputFormat: org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat
#时候有压缩
Compressed: No
#分桶个数,-1表示非桶表
Num Buckets: -1
# 分桶的列
Bucket Columns: []
# 排序的列
Sort Columns: []
Storage Desc Params:
serialization.format 1
Time taken: 0.129 seconds, Fetched: 37 row(s)
'''
------------------------------------------------------------------------------------
SELECT tbl_name,
sum(case when param_key='numRows' then param_value else 0 end) '表的行数',
sum(case when param_key='numRows' then 1 else 0 end) '表的分区数' ,
sum(case when param_key='totalSize' then param_value else 0 end)/1024/1024/1024 '数据量GB',
sum(case when param_key='numFiles' then param_value else 0 end) '文件数'
FROM hive_meta.PARTITIONS pt
inner join PARTITION_PARAMS ptp on pt.PART_ID=ptp.PART_ID
inner join hive_meta.TBLS tbl on pt.TBL_ID= tbl.TBL_ID
---owner,表的拥有者
where tbl_name in ('表名') and owner='虚拟用户'
group by tbl_name
----------------------------------------------------------------------------------------
-- 手动收集表或者分区
analyze table 表名 partition 分区列 compute statistics
运行环境梳理
import requests
url = "https://bigdata-03:8088/ws/v1/cluseter/metrics"
response = requests.get(url)
print response.text
# shell的写法
curl -i https://bigdata-03:8888/ws/v1/cluseter/metrics
cube、grouping set、roll up
-
''' grouping sets 可以按照我们定义的维度(grouping sets的参数)进行分组统计, 就像下面我们定义的维度就是(school,grade),school,grade,(), 也就是说我们定义的什么维度就是什么维度,例如我们这里定义了四个就是四个,定义了一个就是一个 ''' select grouping__id, nvl(school,'全年级'),nvl(grade,'全学校'),count(1) as userCnt from ods.ods_student group by school,grade grouping sets( (school,grade),school,grade,() ) order by grouping__id ; ''' cube 在一个group by 的聚合查询中,将分组字段的全部组合作为维度, 你可以认为是grouping sets的一种特殊情况,我们的分组字段是school,grade, 那我们的cube组合指的就是(school,grade),(school,null),(null,grade),(null,null),这个是等价于(school,grade),school,grade,()的 ''' select grouping__id, nvl(school,'全年级'),nvl(grade,'全学校'),count(1) as userCnt from ods.ods_student group by school,grade with cube order by grouping__id ; ''' rollup 是CUBE的子集,以最左侧的维度为主,从该维度进行层级聚合。左侧为空的时候右侧必须为空 , 例如group by month,day 的rollup 维度是(month,day), (month,null), (null,null) 而group by month,day 的cube 维度是(month,day), (month,null), (null,day),(null,null) ''' select grouping__id, nvl(school,'全年级'),nvl(grade,'全学校'),count(1) as userCnt from ods.ods_student group by school,grade with rollup order by grouping__id ; -- 没有null,grade这个维度
相关规范
- SQL子查询嵌套不宜超过3层
- 少用或者不用Hint,在Hive 2.0后,增强了HiveSQL对成本调优(CBO)的支持,在业务环境变化时,导致Hive无法选择最优的执行计划
- 避免SQL代码的复制粘贴,有多处逻辑一致的代码,可把执行结果放在临时表中
- 在多维统计分析中使用cube、grouping set和roll up起代替多个union all
- 尽量不要用索引,传统关系型数据库中,可以通过索引加速,
- 但是Hive中,经常需要批量处理大量数据,且Hive索引在表和分区有数据更新时不会自动维护,需要手动触发。如果查询的字段不在索引中,会导致整个作业效率更加底下,
- Hive3.0后索引被废除,使用物化视图或者数据存储采用ORC格式代替索引
- 关注NULL值的数据处理
- Hive适合处理宽表(列数多的表),适当冗余有助于Hive的处理性能
- 表以 tb_ 开头,临时表以 tmp_ 开头,视图以 v_ 开头,自定义函数以 udf_ 开头
- 原始数据库 db_org_ 开头,明细表以 db_detail_ 开头 , 数据仓库以 db_dw_ 开头
- 使用set命令,进行配置属性的更改,要有注释
- 代码内不允许包含对表、分区、列的DDL语句,除了新增和删除分区
- Hive SQL更适合处理多条数据组合的数据集
- 保持一个查询语句所处理的表类型单一,比如所有的表都是ORC类型
- SQL表连接的条件列和查询的过滤列最好有分区列和分桶列
- 多层嵌套,内层嵌套表的过滤条件不要写到外层
- 表结构要有注释,列等属性字段需要有注释
- 创建内部表不允许指定数据存储路径,一般统一管理
- 注意分区分桶表的使用
第二章总结
HQL转化为MapReduce的过程
- SQL Parser:Antlr定义SQL的语法规则,完成SQL词法,语法解析,将SQL转化为抽象语法树AST Tree
- HiveLexerX,HiveParser分别是Antlr对语法文件Hive.g编译后自动生成的词法解析和语法解析类
- Semantic Analyer:遍历AST Tree,抽象出查询的基本组成单元QueryBlock
- QueryBlock是一条SQL最基本的组成单元,包括三个部分:输入源,计算过程,输出。简单来讲一个QueryBlock就是一个子查询
- Logical plan: 遍历QueryBlock,翻译为执行操作树OperatorTree
- Hive最终生成的MapReduce任务,Map阶段和Reduce阶段均由OperatorTree组成。逻辑操作符,就是在Map阶段或者Reduce阶段完成单一特定的操作。
- Logical plan optimizer:逻辑层优化器进行OperatorTree变换,减少mapreduce job,减少shuffle数据量
- 谓词下推、合并线性的OperatorTree中partition/sort key相同的reduce (from (select key,value from src group bu key, value)s select s.key group by s.key;
- Map端聚合
- Physical plan遍历OperatorTree,翻译为MapReduce任务
- 物理层优化器进行MapReduce任务的变换,生成最终的执行计划
优化的基本流程
- 选择性能评估项及各自目标,程序的时延和吞吐量
- 如果系统是由多个组件和服务构成,需要分组件和服务定义性能目标
- 明确当前环境下各个组件的性能
- 分析定位性能瓶颈
- 在Hive中最常见的是磁盘和网络I/O的瓶颈,其次是内存会成为一个性能瓶颈。CPU一般比I/O资源相对富余
- 优化产生性能瓶颈的程序或者系统
- 在Hive中,优化方式可以归结为3点,即优化存储、优化执行过程和优化作业的调度。
- 性能监控和告警
- 操作系统层和硬件层 Zabbix 软件层 Ambari 作业层 YARN Timeline
-- explain 分析
hive> explain select s_age,count(1) from student_tb_txt group by s_age;
select s_age,s_sex,count(1) num
from student_tb_orc
group by s_age,s_sex
grouping sets((s_age),(s_age,s_sex))
select s_age,count(distinct s_score) num
from student_tb_orc
group by s_age
--改写后的代码段
select s_age,count(1) num
from(
select s_age,s_score,count(1) num
group by s_age,s_score
) a
-- 不建议
SELECT /*+ MAPJOIN(b) */ a.key, a.value
FROM a JOIN b ON a.key = b.key
SELECT /*+ STREAMTABLE(a) */ a.val, b.val, c.val
FROM a
JOIN b ON (a.key = b.key1)
JOIN c ON (c.key = b.key1)
--开启向量化查询开关
set hive.vectorized.execution.enabled=true;
--开启并行执行
set hive.exec.parallel=true;
set hive.exec.parallel.thread.number=2;
-- 查询元信息
desc formatted student_tb_txt;
--cube、grouping set、roll up区别