hive中reduce类函数说明

Hive数据处理:reduce函数与优化策略
本文详细介绍了Hive中涉及reduce操作的函数,如order by、group by、distribute by、sort by、cluster by等,并探讨了数据倾斜问题及解决方法。此外,还讲解了如何调整reduce个数、合并小文件、优化count(distinct)和group by操作,以提升Hive性能。

 order by 

需要reduce操作,且 只有一个reduce,与配置无关。 数据量很大时,慎用。

 

group by

使用了reduce操作, 受限于reduce数量,设置reduce参数 mapred.reduce.tasks 

输出文件个数与reduce数相同

数据倾斜,优化参数 hive.groupby.skewindata为true,会启动一个优化程序,避免数据倾斜。

 

left semi join

类似 exists。即查找a表中的数据, 是否在b表中存在,找出存在的数据。

 

mapjoin

在map端完成join操作,不需要用reduce,基于内存做join,属于优化操作,其中使用了分布式缓存技术。

mapjoin顾名思义,就是在map端完成join操作,把小数据集分发到map的每台机器上去做join,避免shuffle操作

 

distribute by与group by对比 

都是按key值划分数据 

都使用reduce操作 

**唯一不同的是**distribute by只是单纯的分散数据,而group by把相同key的数据聚集到一起,后续必须是聚合操作。

distribute by也是存在数据倾斜问题的,热点key会被分布到个别reduce节点上

 

order by与sort by 对比 

order by是全局排序 

sort by只是确保每个reduce上面输出的数据有序。如果只有一个reduce时,和order by作用一样。

 

cluster by

把有相同值的数据聚集到一起,并排序。 

效果等价于distribute by col sort by col 

cluster by col <==> distribute by col sort by col

 

union all

多个表的数据合并成一个表,hive不支持union


是不是map数越多越好?  

答案是否定的。如果一个任务有很多小文件(远远小于块大小128m),则每个小文件也会被当做一个块,用一个map任务来完成, 

而一个map任务启动和初始化的时间远远大于逻辑处理的时间,就会造成很大的资源浪费。 

而且,同时可执行的map数是受限的。

 

1.Hive自己如何确定reduce数:  

reduce个数的设定极大影响任务执行效率,不指定reduce个数的情况下,Hive会猜测确定一个reduce个数,基于以下两个设定: 

hive.exec.reducers.bytes.per.reducer(每个reduce任务处理的数据量,默认为1000^3=1G)  

hive.exec.reducers.max(每个任务最大的reduce数,默认为999) 

计算reducer数的公式很简单N=min(参数2,总输入数据量/参数1) 

即,如果reduce的输入(map的输出)总大小不超过1G,那么只会有一个reduce任务; 

如:select pt,count(1) from popt_tbaccountcopy_mes where pt = '2012-07-04' group by pt;  

总大小为9G多,因此这句有10个reduce 

 

2.调整reduce个数方法一:  

调整hive.exec.reducers.bytes.per.reducer参数的值; 

set hive.exec.reducers.bytes.per.reducer=500000000; (500M) 

这次有20个reduce 

          

3.调整reduce个数方法二;  

set mapred.reduce.tasks = 15; 

这次有15个reduce 

 

 

4.reduce个数并不是越多越好;  

同map一样,启动和初始化reduce也会消耗时间和资源; 

另外,有多少个reduce,就会有多少个输出文件,如果生成了很多个小文件,那么如果这些小文件作为下一个任务的输入,则也会出现小文件过多的问题; 

 

5.什么情况下只有一个reduce;  

很多时候你会发现任务中不管数据量多大,不管你有没有设置调整reduce个数的参数,任务中一直都只有一个reduce任务; 

其实只有一个reduce任务的情况,除了数据量小于hive.exec.reducers.bytes.per.reducer参数值的情况外,还有以下原因: 

a)没有group by的汇总,比如把select pt,count(1) from popt_tbaccountcopy_mes where pt = '2012-07-04' group by pt;

写成 select count(1) from popt_tbaccountcopy_mes where pt = '2012-07-04'; 

这点非常常见,希望大家尽量改写。 

b)用了order by,全局排序

c)有笛卡尔积 

通常这些情况下,除了找办法来变通和避免,我暂时没有什么好的办法,因为这些操作都是全局的,所以hadoop不得不用一个reduce去完成; 

同样的,在设置reduce个数的时候也需要考虑这两个原则:使大数据量利用合适的reduce数;使单个reduce任务处理合适的数据量;

 

3.怎样写exist/in子句?

Hive不支持where子句中的子查询,SQL常用的exist in子句需要改写。这一改写相对简单。考虑以下SQL查询语句:

SELECT a.key, a.value  FROM a  WHERE a.key in  (SELECT b.key  FROM B);

可以改写为

SELECT a.key, a.value  FROM a LEFT OUTER JOIN b ON (a.key = b.key)  WHERE b.key <> NULL;

一个更高效的实现是利用left semi join改写为:

SELECT a.key, a.val  FROM a LEFT SEMI JOIN b on (a.key = b.key);

 

1、如何合并小文件,减少map数?

如果一个表中的map数特别多,可能是由于文件个数特别多,而且文件特别小照成的,可以进行如下操作,合并文件,:

set mapred.max.split.size=100000000; // 100M

set mapred.min.split.size.per.node=100000000;

set mapred.min.split.size.per.rack=100000000;

set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat; //  合并小文件

 

2、如何适当的增加map数?distribute by

如果表A只有一个文件,大小为120M,包含几千万记录,可以考虑用多个map任务完成

set mapred.reduce.tasks=10;

create table a_1 as

select * from a

distribute by rand(123); //将a表的记录,随机的分散到包含10个文件的a_1表中

 

3.如果设计和使用bucket

所用场合:对某一列进行分区,比如对用户ID进行分区,例如:

CREATE TABLE weblog (user_id INT, url STRING, source_ip STRING)

PARTITIONED BY (dt STRING)

CLUSTERED BY (user_id) INTO 96 BUCKETS; // 按照日期分区后,再按照user_id把日志放在96个篮子里。

 

4.count(distinct)

当count distinct 的记录非常多的时候,设置以下两个参数:

hive>  hive.map.aggr = true    — 在map端进行combine

hive> set hive.groupby.skewindata=true;

hive>  select count (distinct gid) from cookie_label_summary where i_date=20130924;

 

5.group by

map端聚合,提前一部分计算:hive.map.aggr = true 同时设置间隔:hive.groupby.mapaggr.checkinterval

均衡处理:hive.groupby.skewindata

这是针对数据倾斜的,设为ture的时候,任务的reduce会把原来一个job拆分成两个,第一个的job中reduce处理处理不同的随即分发过来的key的数据,生成中间结果,再由最后一个综合处理。

(解决group by的数据倾斜主流就是设置这个参数,相当于先用一组随机key先汇总一次,然后去掉随机数继续汇总,注意这个参数是专门为group by设计的)

 

6.order by, sort by ,distribute by, cluster by

order by VS Sort by: 

order by是在全局的排序,只用一个reduce去跑,所以在set hive.mapred.mode=strict 模式下,order by 必须limit,否则报错。Sort by只保证同一个reduce下排序正确。

Distribute by with sort by: 

Distribute by 是按指定的列把map 输出结果分配到reduce里(经常用来增加map数)。所以经常和sort by 来实现对某一字段的相同值分配到同一个reduce排序。

Cluster by 实现了Distribute by+ sort by 的功能

先cluser by,最后再order by,真的会提供整体排序效率吗?

未实验验证,从原来上看是的,先在map端局部排序

所以面试官问order by造成的数据倾斜如何解决时,应该就是要回答这一点,先distribute在map端就把数据倾斜解决掉,然后map端排序  

### Hive 中小时新增计算的实现方式 在 Hive 中,要实现按小时统计新增记录的功能,通常需要结合时间戳字段以及聚合函数来完成。以下是具体的实现方法: #### 方法一:基于 `GROUP BY` 和日期时间函数 Hive 提供了一系列的时间处理函数,例如 `from_unixtime()`、`hour()` 等,这些函数可以帮助我们提取时间中的小时部分并进行分组统计。 假设有一张表 `events`,其中包含一个时间戳字段 `event_time` 和其他相关字段。为了按照每个小时统计新增记录数量,可以执行以下查询: ```sql SELECT hour(from_unixtime(cast(event_time as bigint))) AS event_hour, COUNT(*) AS new_records_count FROM events WHERE event_time >= UNIX_TIMESTAMP('2023-10-01 00:00:00') -- 设置起始时间 AND event_time < UNIX_TIMESTAMP('2023-10-02 00:00:00') -- 设置结束时间 GROUP BY hour(from_unixtime(cast(event_time as bigint))); ``` 此查询中: - 使用 `from_unixtime()` 将 Unix 时间戳转换为可读格式。 - 使用 `hour()` 提取时间中的小时部分[^1]。 - 使用 `COUNT(*)` 对每一小时内发生的事件计数。 --- #### 方法二:利用窗口函数进行更复杂的分析 如果不仅需要统计每小时的新增记录,还需要进一步比较不同时间段的变化趋势,则可以引入窗口函数。例如,使用 `ROW_NUMBER()` 或 `LAG()` 来对比前一个小时的数据变化情况。 示例 SQL 如下: ```sql WITH hourly_stats AS ( SELECT hour(from_unixtime(cast(event_time as bigint))) AS event_hour, COUNT(*) OVER (PARTITION BY hour(from_unixtime(cast(event_time as bigint)))) AS records_in_hour FROM events WHERE event_time >= UNIX_TIMESTAMP('2023-10-01 00:00:00') AND event_time < UNIX_TIMESTAMP('2023-10-02 00:00:00') ) SELECT event_hour, LAG(records_in_hour, 1) OVER (ORDER BY event_hour) AS prev_hour_records, records_in_hour AS current_hour_records, records_in_hour - LAG(records_in_hour, 1) OVER (ORDER BY event_hour) AS diff_with_prev_hour FROM hourly_stats; ``` 在此查询中: - 首先通过子查询 `hourly_stats` 获取每小时的记录总数。 - 使用 `LAG()` 函数获取上一个小时的记录数,并计算差值以反映增长或减少的情况[^2]。 --- #### 方法三:自定义 UDF 实现复杂逻辑 对于某些特定需求,可能无法仅靠内置函数满足业务场景。此时可以通过编写自定义 UDF(User Defined Function)扩展功能。 例如,创建一个名为 `HourExtractor` 的 UDF 来专门解析时间戳并返回对应的小时值。具体步骤如下: 1. 编写 Java 实现 UDF 功能; 2. 打包上传到 HDFS 并注册该函数; 3. 在 Hive 查询中调用新定义的 UDF。 代码片段展示如何定义简单的 UDF: ```java package org.hive_test.udf; import org.apache.hadoop.hive.ql.exec.UDF; import java.sql.Timestamp; public class HourExtractor extends UDF { public Integer evaluate(Timestamp ts) { if (ts == null) return null; return ts.getHours(); } } ``` 随后将其编译打包并通过命令加载到 Hive 中: ```sql ADD JAR hdfs:///path/to/HourExtractor.jar; CREATE TEMPORARY FUNCTION extract_hour AS 'org.hive_test.udf.HourExtractor'; ``` 最终可以在查询语句中这样使用: ```sql SELECT extract_hour(event_time) AS event_hour, COUNT(*) AS record_count FROM events GROUP BY extract_hour(event_time); ``` --- ### 总结 以上三种方法分别适用于不同的应用场景: - **基础统计**:推荐直接采用标准 SQL 的 `GROUP BY` 加上时间函数的方式。 - **高级分析**:考虑加入窗口函数提升数据分析能力。 - **定制化需求**:开发专属 UDF 解决特殊问题。 无论哪种方案都需注意性能优化,比如合理设置 Reduce 数量以应对大数据集带来的压力[^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值