hivesql优化(Hive优化:严格模式)

本文目录
Hive优化:严格模式
Hive提供了一个严格模式,可以防止用户执行那些可能意想不到的不好的影响查询。
通过设置属性hive.mapred.mode值为默认是非严格格式nonstrict。开启严格模式需要修改hive.mapred.mode值为strict,开启严格模式可以禁止3钟类型的查询。
--设置非严格模式(默认)
set hive.mapred.mode=nonstrict;
--设置严格模式
set hive.mapred.mode=strict;
(1)对于分区表,除非where语句中含有分区字段过滤条件来限制范围,否则不允许执行
--设置严格模式下 执行sql语句报错;非严格模式下的可以的
select * from order_partition;
异常信息:Error :Error while compliling statement:FAILED:SemanticException :No parttion predicate found for Alias "order_partition" Table "order_partition"
(2)对于使用了order by 语句的查询,要求必须使用limit语句
--设置严格模式下 执行sql语句报错;非严格模式下可以的
select * from order_partition where month=’2019-03’ order by order_price;
异常信息:Error Error while compiling statement:FAILED:SemanticException 1:61 In strict mode,if ORDER BY is specified ,LIMIT must also be specifiied.Error encountered near token ’order_price’
(3)限制笛卡尔积的查询
严格模式下,避免出现笛卡尔积的查询
5种让Hive查询变快的方法
在过去几年中,主要受到围绕Stinger计划的Hive社区创新的推动,Hive查询时间得到了显着改善,使Hive能够以速度和规模支持批量和交互式工作负载。
但是,许多使用者仍然不熟悉以最快速度运行Hive查询的基本技术和最佳实践。本文中,将重点介绍一些常使用的简单技术,以提高HIVE查询的性能。
Hive可以使用Apache Tez执行引擎而不是Map-reduce引擎。不会详细介绍这里提到的使用Tez的许多好处; 相反,提出一个简单的建议:如果在您的环境中默认情况下没有打开它,请在Hive查询的开头使用Tez设置为“true”
Hive支持ORCfile,这是一种新的表存储格式,通过谓词下推,压缩等技术实现极佳的速度提升。
对每个HIVE表使用ORCFile应该是一个明智的选择,对于获得HIVE查询的快速响应时间非常有益。
作为一个例子,考虑两个大表A和B(存储为文本文件,这里没有指定一些列),以及一个简单的查询 :
此查询可能需要很长时间才能执行,因为表A和B都存储为TEXT。将这些表转换为ORCFile格式通常会显着缩短查询时间:
ORC支持压缩存储(使用ZLIB或如上所示使用SNAPPY),但也支持未压缩存储。
将基表转换为ORC通常是取决于所在团队获取数据的职责,由于其他优先级,可能需要一些时间来更改完整的获取数据过程。ORCFile的好处是如此明显,以至于推荐如上所示的自助式方法 - 将A转换为A_ORC,将B转换为B_ORC并以此方式进行连接,以便立即从更快的查询中受益,而不依赖于其他团队。
矢量化查询执行通过一次批量执行1024行而不是每行一行来提高扫描,聚合,过滤器和连接等操作的性能。
这个功能在Hive 0.13中引入,显着缩短了查询执行时间,并且可以通过两个参数设置轻松启用:
在提交最终执行之前,Hive会优化每个查询的逻辑和物理执行计划。这些优化不是基于查询的成本 - 也就是说,直到运行时。
最近添加到Hive,基于成本的优化,基于查询成本执行进一步优化,从而导致可能不同的决策:如何订购联接,执行哪种类型的联接,并行度等。
要使用基于成本的优化(也称为CBO),请在查询开头设置以下参数
然后,通过运行Hive的“analyze”命令为CBO准备数据,以收集我们想要使用CBO的表的各种统计信息。
例如,在tweet数据表中,希望收集有关该表的统计信息以及大约2列:“sender”和“topic”:
使用HIVE 0.14(在HDP 2.2上),analyze命令的工作速度要快得多,而且您不需要指定每一列,因此只需如下:
现在使用此表执行查询应该会导致不同的执行计划由于成本计算和Hive创建的不同执行计划而更快。
SQL是一种强大的声明性语言。与其他声明性语言一样,编写SQL语句的方法不止一种。尽管每个语句的功能都相同,但它可能具有截然不同的性能特征
每条记录代表一次点击事件,希望找到每个sessionID的最新网址。
有人使用如下方式:
在上面的查询中,构建一个子查询来收集每个会话中最新事件的时间戳,然后使用内部联接来过滤掉其余的事件。
虽然查询是一个合理的解决方案 - 从功能的角度来看 - 事实证明,有一种更好的方法来重写这个查询,如下所示
在这里,使用Hive的OLAP功能(OVER和RANK)来实现相同的功能,但没有使用表连接。
显然,删除不必要的连接几乎总能带来更好的性能,而且当使用大数据时,这比以往任何时候都更重要。在很多情况下查询不是最优的 - 所以仔细查看每个查询并考虑重写是否可以使它更好更快。
***隐藏网址***
hive优化方法
1、列裁剪和分区裁剪
2、谓词下推
3、sort by 替换order by
4、group by 代替distinct
5、group by 配置调整
map 端预聚合:set hive.map.aggr=true ; set hive.groupby.mapaggr.checkinterval=100000
倾斜均衡配置项:set hive.groupby.skewindate=true
6、join优化
6.1 大小表,小表前置
6.2 多表Join时key相同
6.3 利用mapjoin特性
6.4 分桶表 mapjoin
6.5 倾斜均衡配置项:
设置hive.optimize.skewjoin=true,开启后,在join过程中hive会将计数超过阈值hive.skewjoin.key(默认100000)的倾斜key对应的行临时写进文件中,然后再启动另一个job做map join生成结果。通过hive.skewjoin.mapjoin.map.task参数还可以控制第二个job的mapper数量,默认10000.
6.7 优化sql处理join数据倾斜
6.7.1、空值或无意义的值:若不需要空值数据,就提前写到where语句过滤掉,如果需要保留的话,将空值key用随机方式打散。
6.7.2、单独处理倾斜key
6.7.3、不同数据类型,join的关联字段类型不一样,导致耗时长,所以需要注意做类型转换(join关联字段,一个是int,一个是string,,计算key的hash值时默认时int类型做的,这样导致所有真正的string类型的key都分配到一个reducer上了)
6.7.4、mapjoin的小表比较大的时候,,无法直接使用mapjoin,则 select/*+mapjoin(b)*/ from a left join ( select /*+mapjoin (b)*/ from b inner join a )
6.8、mapreduce 优化
6.8.1、调整mapper数
mapper数量与输入文件的split数息息相关。如果想减少mapper数,就适当提高mapred.min.split.size,split数就减少了;如果想增大mapper数,除了降低maperd.min.split.size之外,也可以提高mapred.map.task;一般来讲,如果输入文件是少量大文件,就减少mapper数;如果是大量非小文件,就增大mapper数,如果是小文件,就喝吧小文件
6.8.2 调整reducer数
使用参数mapred.reduce.task可以直接设定reducer数量,如果不设置的话,hive会自行推测,推测逻辑如下:
参数hive.exec.reducers.bytes.per.reducer.设定每个reducer能够处理的最大的数据量,默认时1G
参数hive.exec.reducers.max设定每个job的最大的reducer数量,默认时999或者1009
得到reducer数:reducers_num=min(total_input_size/reducers.bytes.per.reducer,reducers.max)
reducer数量与输出文件的数量相关,如果reducer数太多,会产生大量小文件,对hdfs造成压力,如果reducer数太少,每个reducer要处理很多数据,容易拖慢运行时间或造成oom
6.8.3 合并小文件
输入阶段合并:需要更改hive的输入文件格式,即参数hive.input.format,默认值是org.apache.hadoop.hive.ql.io.hiveiputformat,我们该成org.apache.hadoop.hive.ql.io.combinehiveinputformat.
这样比调整mapper数时,又多出两个参数,分别是mapred.min.split.size.per.node和mapred.min.split.size.per.rack,含义是单节点和单机架上的最小split大小。如果发现又split大小小于这两个默认值100MB,则会进行合并
输出阶段合并:直接将hive.merge.mapfiles和hive.merge.mapredfiles都设置为true即可。
hive.merge.mapfiles表示将map-only任务的输出合并
hive.merge.mapredfiles表示将map-reduce任务的输出合并
另外hive.merge.size.per.task可以指定每个task输出后合并文件大小的期望值
hive.merge.size.smallfiles.avgsize可以指定所有输出文件大小的均值阈值,默认时1G
6.9 启用压缩
压缩job的中间结果数据和输出数据,可以少量CPU时间节省出很多空间。压缩方式一般选择snappy,效率最高,要启用中间压缩,需要设定:
hive.exec.compress.intermediate为true,同时指定压缩方式hive.intermediate.compression.code为org.apache.hadoop.io.compress.snappycode.
另外,参数hive.intermediate.compression.type可以选对块block还是对记录record压缩,block压缩率比较高,输出压缩的配置基本相同:打开hive.exec.compress.output
6.10 jvm重用
在mr job中,默认时每执行一个task就会启动一个jvm,如果task非常小而且碎,那么jvm启动和关闭耗时都会比较长。可以通过调节参数mapred.job.reuse.jvm.num.task来重用。例如将这个参数设置为5,那么久代表同一个mr job中顺序执行的5各task可以重复使用一个jvm,减少启动和关闭开销,但是他对不同mr job的task无效
6.11 采用合适的存储格式
6.12 数据倾斜
数据倾斜原因:1、join 倾斜 2、聚合倾斜
group by 倾斜: group by 字段中某个字段的值过多,导致处理这个值得reduce耗时很久
解决方法:set hive.map.aggr=true; -- 表示开启map端聚合
set hive.groupby.skewindata=true; 注意:只能对单个字段聚合 , 生成两个map job ,第一个map job中map输出结果随机分布到reduce中,每个reduce做部分聚合操作,并输出结果,这样相同的groub key有可能被分发到不同的reduce中,从而达到负载均衡的目的;第二个map job再根据预处理的数据结果按照group by key分布到reduce中,这个过程可以保证相同的key被分到同一个reduce中,最后完成最终的聚合操作
6.13 优化in、exists语句
使用left semi join 替换in 、exists
6.14 合并 mapreduce操作
multi-group by 是hive的一个非常好的特性,它使得hive中利用中间结果变更非常方便
例如:
from ( select a.status,b.school from status_update a join profilees b on (a.userid=b.suerid)) subq1
insert overwirte table a1 partition(ds=’2021-03-07’)
select subq1.school,count(1) group by subq1.school
insert overwrite table b1 partition(ds=’2021-03-07’)
select subq1.status,count(1) group by subq1.status
上述语句使用了multi-group by特性联系group by 2次数据,使用不同的group by key,这一特性可以减少一次mapreduce操作
hive数据倾斜及处理
火山日常啰嗦
学习了一些大数据的相关框架后,发现应用层的东西确实不难,真正难的都是底层原理,所以我查看了很多资料,借鉴了前人的方法再加上自己的理解,写下了这篇文章。
数据倾斜的直白概念:
数据倾斜就是数据的分布不平衡,某些地方特别多,某些地方又特别少,导致的在处理数据的时候,有些很快就处理完了,而有些又迟迟未能处理完,导致整体任务最终迟迟无法完成,这种现象就是数据倾斜。
针对mapreduce的过程来说就是,有多个reduce,其中有一个或者若干个reduce要处理的数据量特别大,而其他的reduce处理的数据量则比较小,那么这些数据量小的reduce很快就可以完成,而数据量大的则需要很多时间,导致整个任务一直在等它而迟迟无法完成。
跑mr任务时常见的reduce的进度总是卡在99%,这种现象很大可能就是数据倾斜造成的。
产生数据倾斜的原因:
1) key的分布不均匀或者说某些key太集中。
上面就说过,reduce的数据量大小差异过大,而reduce的数据是分区的结果,分区是对key求hash值,根据hash值决定该key被分到某个分区,进而进入到某个reduce,而如果key很集中或者相同,那么计算得到它们的hash值可能一样,那么就会被分配到同一个reduce,就会造成这个reduce所要处理的数据量过大。
2) 业务数据自身的特性。
比如某些业务数据作为key的字段本就很集中,那么结果肯定会导致数据倾斜啊。
还有其他的一些原因,但是,根本原因还是key的分布不均匀,而其他的原因就是会造成key不均匀,进而导致数据倾斜的后果,所以说根本原因是key的分布不均匀。
既然有数据倾斜这种现象,就必须要有数据倾斜对应的处理方案啊。
简单地说数据倾斜这种现象导致的任务迟迟不能完成,耗费了太多时间,极大地影响了性能,所以我们数据倾斜的解决方案设计思路就是往如何提高性能,即如何缩短任务的处理时间这方面考虑的,而要提高性能,就要让key分布相对均衡,所以我们的终极目标就是考虑如何预处理数据才能够使得它的key分布均匀。
常见的数据倾斜处理方案:
1 设置参数
1)设置hive.map.aggr=true //开启map端部分聚合功能,就是将key相同的归到一起,减少数据量,这样就可以相对地减少进入reduce的数据量,在一定程度上可以提高性能,当然,如果数据的减少量微乎其微,那对性能的影响几乎没啥变化。
2)设置hive.groupby.skewindata=true //如果发生了数据倾斜就可以通过它来进行负载均衡。当选项设定为 true,生成的查询计划会有两个 MR Job。第一个 MR Job 中,Map 的输出结果集合会随机分布到 Reduce 中,每个 Reduce 做部分聚合操作,并输出结果,这样处理的结果是相同的Key 有可能被分发到不同的 Reduce 中,从而达到负载均衡的目的;第二个 MR Job 再根据预处理的数据结果按照Key 分布到 Reduce 中(这个过程是按照key的hash值进行分区的,不同于mr job1的随机分配,这次可以保证相同的Key 被分布到同一个 Reduce 中),最后完成最终的聚合操作。所以它主要就是先通过第一个mr job将key随机分配到reduce,使得会造成数据倾斜的key可能被分配到不同的reduce上,从而达到负载均衡的目的。到第二个mr job中,因为第一个mr job已经在reduce中对这些数据进行了部分聚合(就像单词统计的例子,a这个字母在不同的reduce中,已经算出它在每个reduce中的个数,但是最终的总的个数还没算出来,那么就将它传到第二个mr job,这样就可以得到总的单词个数),所以这里直接进行最后的聚合就可以了。
3)hive.exec.reducers.bytes.per.reducer=1000000000 (单位是字节)
每个reduce能够处理的数据量大小,默认是1G
4)hive.exec.reducers.max=999
最大可以开启的reduce个数,默认是999个
在只配了hive.exec.reducers.bytes.per.reducer以及hive.exec.reducers.max的情况下,实际的reduce个数会根据实际的数据总量/每个reduce处理的数据量来决定。
5)mapred.reduce.tasks=-1
实际运行的reduce个数,默认是-1,可以认为指定,但是如果认为在此指定了,那么就不会通过实际的总数据量/hive.exec.reducers.bytes.per.reducer来决定reduce的个数了。
2 sql语句优化
给几个具体的场景以及在这些场景下的处理方案:
1)进行表的join这种业务操作时,经常会产生数据倾斜。
原因就是这些业务数据本就存在key会分布不均匀的风险,所以我们join时不能使用普通的join(reduce端join)或者可以使用普通join,但是是优化后的。
但是这种操作有个前提条件就是仅适用于小表join大表,而小表怎么定义它的大小,多小的表才算小表,这里有个参数可以确定的(但是这个参数名我暂时忘记了),如果小表的数据大小小于这个值,就可以使用map join,而是在这种情况下是自动使用map join这种方案的。所以如果是大小表join,直接用map join,避免数据倾斜。
方法1:(普通join)
select * from log a join users b on (a.user_id is not null and a.user_id = b.user_id );
这是属于表的内连接的,两张表不满足条件的记录都不保留。
方法2:检测到user_id是null时给它赋予一个新值(这个新值由一个字符串(比如我自己给它定一个 hive)加上一个随机数组成),这样就可以将原来集中的key分散开来,也避免了数据倾斜的风险。而且因为这些数据本来就是无效数据,根本不会出现在结果表中,所以,这样处理user_id(由一个字符串(比如我自己给它定一个 hive)加上一个随机数),它也无法关联的,因为有效的数据的user_id没有这种形式的,所以就算这些无效数据出现在不同的reduce中还是不会影响结果的,我这样处理只是为了将它们分散开而已,所以用这种方法处理,结果表中也不会出现null这些无效数据,跟过滤处理方案得到的结果是一样的。(普通join)
select *
from log a
join users b
on case when a.user_id is null then concat(‘hive’,rand() ) else a.user_id end = b.user_id;
但是这两种方案只是适用于大表join大表的内连接,两张表的无效数据都不保留。
但是如果对于左外连接或者右外连接这种情况,即使驱动表中某些记录在另一张表中没有数据与它对应,但我们是依然需要保留驱动表的这些数据的,那该怎么办呢?其实很简单,只需要将上述方法得到的结果再与驱动表的这些无数据取并集就可以了。
如下:
select * from log a
left outer join users b
on a.user_id is not null
and a.user_id = b.user_id
union all
select * from log a
where a.user_id is null;
2)虽然都是大表,但是呢对于某些业务数据而言,其有用的部分只占它所在表的很少一部分,那么我们就可以将它们先取出来,得到的结果应该是一张小表,那么就可以使用map join来避免数据倾斜了。
场景:用户表中user_id字段为int,log表中user_id字段既有string类型也有int类型。
当按照user_id进行两个表的Join操作时,因为我们在连接时要进行user_id的比较,所以需要user_id的类型都相同,如果我们选择将log表中的String类型转换为int类型,那么就可能会出现这种情况:String类型转换为int类型得到的都是null值(这就是类型转换的问题了,String类型数据转换为int类型会失败,数据丢失,就会赋null值),如果所有的String类型的user_id都变成了null,那么就又出现了集中的key,分区后就又会导致数据倾斜。所以我们进行类型转换时不能选择将String类型转换为int,而应该将int类型转换为String,因为int转换为String不会出问题,int类型原来的值是什么,转换为String后对应的字符串就会是什么,形式没变,只是类型变了而已。
解决方法:把int类型转换成字符串类型
select * from users a
join logs b
on (a.usr_id = cast(b.user_id as string));
比如有一份日志,要你从日志中统计某天有多少个用户访问网站,即统计有多少个不同的user_id;但是呢这个网站却又恰巧遭到攻击,日志中大部分都是同一个user_id的记录,其他的user_id属于正常访问,访问量不会很大,在这种情况下,当你直接使用count(distinct user_id)时,这也是要跑mr任务的啊,这时这些大量的相同的user_id就是集中的key了,结果就是通过分区它们都被分到一个reduce中,就会造成这个reduce处理的数据特别大,而其中的reduce处理的数据都很小,所以就会造成数据倾斜。
那么要怎么优化呢?
方法1:可以先找出这个user_id是什么,过滤掉它,然后通过count(distinct user_id)计算出剩余的那些user_id的个数,最后再加1(这1个就是那个被过滤掉的user_id,虽然它有大量的记录,但是ser_id相同的都是同一个用户,而我们要计算的就是用户数)
sql语句展示:
分组求和后降序排序,就可以得到这个数据量最大的user_id是什么,然后我们下一步操作时就过滤它,等计算完其他的再加上它这一个。
select user_id,count(user_id) from log group by user_id desc limit 2;
select count(distinct user_id)+1 as sum from log;
sum就是最终的结果--用户数
方法2:我们可以先通过group by分组,然后再在分组得到的结果的基础之上进行count
sql语句展示:
select count(*) from (select user_id from log group by user_id) new_log;
总的来说就是,数据倾斜的根源是key分布不均匀,所以应对方案要么是从源头解决(不让数据分区,直接在map端搞定),要么就是在分区时将这些集中却无效的key过滤(清洗)掉,或者是想办法将这些key打乱,让它们进入到不同的reduce中。
性能调优是指通过调整使得机器处理任务的速度更快,所花的时间更少,而数据倾斜的处理是hive性能调优的一部分,通过处理能够大大减少任务的运行时间。
除了数据倾斜的处理之外,hive的优化还有其他方面的,例如where子句优化:
select * from a left outer join b on (a.key=b.key) where a.date=’2017-07-11’ and b.date=’2017-07-11’;
这是一个左外连接。
这个sql语句执行的结果是:得到的结果是表a与表b的连接表,且表中的记录的date都是’2017-07-11’。
而这个sql语句的执行过程是:逐条获取到a表的记录,然后扫描b表,寻找字段key值为a.key的记录,找到后将b表的这条记录连接到a表上,然后判断连接后的这条记录是否满足条件a.date=’2017-07-11’ and b.date=’2017-07-11’,如果满足,则显示,否则,丢弃。
因为这是一个左外连接,且a为驱动表,连接时在a中发现key而在b中没有发现与之相等的key时,b中的列将置为null,包括列date,一个不为null,一个为null,这样后边的where就没有用了。
简答的说这个方案的做法就是先按连接条件进行连接,连接后再看where条件,如果不满足就丢弃,那之前连接所做的那些功夫就浪费了,白白耗费了资源(cpu等),增加了运行的总时间,如果有一种方案可以在未进行连接之前就直接判断出不满足最终的条件,那么就可以直接丢弃它,这样对于这样的记录就不要浪费资源以及时间去连接了,这样也是能提升性能的,下面就看看这种方案:
sql语句:
将刚才的where限制条件直接放到on里面,那么就变成了满足这三个条件才会进行连接,不满足的直接过滤掉,就像上面所说的,少了无效连接那一步,就相对地节约了时间,如果这样的无效连接的记录很多的话,那么采用这种改进版的方案无疑能够较大程度地提高性能。
select * from a left outer join b on (a.key=b.key and a.date=’2017-07-11’ and b.date=’2017-07-11’);
不管怎么说,我们在运行任务时,总是希望能加快运行速度,缩短运行时间,更快地得到结果,即提升性能,这是我们的目的,这就是我们所谓的性能调优。
关于小表join大表的补充:
表join时的操作是这样的:
当操作到驱动表的某条记录时,就会全局扫描另一张表,寻找满足条件的记录,而当扫描它时,为了读取速度更快,一般都选先将它加载到内存,而内存的大小是有限的,为了不占据过多的内存或者避免内存溢出,加载进入内存的表一般是小表,即数据量比较小,map join就是这样做的。
即驱动表不放进内存,而另一张表(即要连接到驱动表的那一张表)就要先加载进内存,为了扫描速度更快,提高性能。
比如select * from a left outer join b on (a.key=b.key);
左外连接,驱动表是a,表b的记录是要被连接到表a的,所以每在a上连接一条记录就要被全局扫描一次的表是b,所以表b应先加载到内存(前提是表b是小表,如果是大表的话,估计会产生oom异常--out of memory内存溢出异常)。
select * from aa right outer join bb on (a.key=b.key);
右外连接,驱动表是bb,aa应先加载到内存(前提是小表)。
ps:希望我的分享能帮助到有需要的伙伴哦。我不是大神的哦,如果文中有误,还请大家不吝赐教,帮忙指正,谢谢了!!!
hive sql 优化的常用手段有哪些
1、join连接时的优化:当三个或多个以上的表进行join操作时,如果每个on使用相同的字段连接时只会产生一个mapreduce。
2、join连接时的优化:当多个表进行查询时,从左到右表的大小顺序应该是从小到大。原因:hive在对每行记录操作时会把其他表先缓存起来,直到扫描最后的表进行计算
3、在where字句中增加分区过滤器。
4、当可以使用left semi join 语法时不要使用inner join,前者效率更高。原因:对于左表中指定的一条记录,一旦在右表中找到立即停止扫描。

更多文章:
androidapp源码免费下载(如何实现APK的反编译得到APK的源码)
2026年9月22日 18:00
service pack 3(操作系统版本升级(SP) Service Pack 3当中的“Service Pack 3”是什么意思)
2026年9月22日 10:20
html代码怎么写大佬教程(html网页的题来个大佬,写代码,题目在图上)
2026年9月22日 10:10
结构体内又一个struct(c++ 在结构体中再嵌入一个结构体如何调用)
2026年9月22日 09:40
cocos creator中文(cocoscreator和cocoscreator3d的区别)
2026年9月22日 02:30






