spark 通过打散热点key解决数据倾斜问题

本文介绍了一种在Spark中处理热点key数据倾斜的方法,并通过一个实际案例演示如何通过打散热点key并重新聚合来优化计算过程,从而提高任务运行效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

项目github地址:bitcarmanlee easy-algorithm-interview-and-practice
欢迎大家star,留言,一起学习进步

1.热点key的数据倾斜

在大数据相关的统计与处理中,热点key造成的数据倾斜非常常见也非常讨厌,经常会造成job运行时间变长或者造成job的OOM最后导致任务失败。例如在wordcount任务中,如果有一个word是热点词,出现的次数很多,那么最后这个job的运行时间就是由这个热点词所在的task运行时间决定的。因此遇到这种热点问题,我们需要想办法改进代码,优化任务,提高最终的运行效率。

2.实际case

现在有这么一个简单的实际例子:
hdfs上有一个名为"xxx"的路径,此路径下的数据量比较大,有几百G之多。现在我们想统计一下这个路径下所有文件的行数。
如果数据量不大,在spark-shell中,可以用一行简单的代码解决问题:

scala> sc.textFile("xxx").count()

但是数据量大了以后,运行的速度很慢很慢,慢到不可接受;而且最后程序会报OOM退出,得不到最终的结果。那怎么办呢?

3.通过将热点key打算做计算

我们将上述需求稍微做一下转型:
统计所有数据的行数,假设每一行对应的一个key就是"all",每一行的输出是"all, 1",最后需要做的就是简单的wordcount,针对all这个热点key,然后求和!
这种我们明确知道热点key是啥的case,一般的做法是将热点key先打散,然后再聚回来!
直接上代码:

    def linestats(sc: SparkContext) = {
        val inputpath = "xxx"
        sc.textFile(inputpath)
            .map(x => {
                val randomNum = (new java.util.Random).nextInt(2000)
                val allkey = randomNum + "_all"
                (allkey, 1)
            })
            .reduceByKey((x, y) => x + y)
            .map(x => {
                val (keywithrandom, num) = (x._1, x._2)
                val key = StringUtils.split(keywithrandom, "_")(1)
                (key, num.toLong)
            })
            .reduceByKey((x, y) => x + y)
            .map(x => "%s\t%s".format(x._1, x._2))
            .repartition(1)
    }

上面代码的思路如下:
1.第一步先将key打算,给所有"all"加上一个随机前缀。
2.然后对带有随机前缀的key做第一次聚合,即reduceByKey操作,得出第一次聚合的结果。
3.再将随机前缀去掉,做第二次聚合,即reduceByKey操作,得到最终的结果!

<think>嗯,用户想了解Hive on Spark中如何避免数据倾斜问题,这需要结合多个优化策略来解决。从用户提供的引用资料来看,数据倾斜主要出现在分组聚合和Join操作中,解决方案涉及参数调整、SQL重写和特殊算法应用。 用户可能正在处理大规模数据作业,遇到了任务卡顿或OOM问题。ta的引用[3]和[4]明确提到了Executor丢失和单个Task执行过久的现象,这是典型的数据倾斜症状。需要给ta既讲原理又给实操方案。 先梳理核心优化方向:分组聚合倾斜可以通过map端预聚合和倾斜键分离处理;Join倾斜则重点考虑Map Join和分桶Join。用户引用[2]的第六章详细讨论了Sort Merge Bucket Map Join,这个高级特性值得重点说明。 参数配置方面,要强调几个关键点:skewjoin参数必须开启,reduce并行度要合理增加,小文件合并能预防倾斜恶化。用户引用[1]的Cloudera文档提到hive.optimize.skewjoin.compiletime参数,这个对静态数据倾斜特别有效。 特别要注意的是,用户引用[4]的Spark UI诊断方法很实用,应该纳入回答。当看到某个stage里个别task运行时间远超其他时,基本可以锁定倾斜发生在该shuffle阶段。 最后给出具体示例很重要,比如用distribute by rand()分散倾斜键的方法,用户引用[3]的第六条也提到reduce数量设置。示例代码要展示如何将倾斜打散再聚合,这种实战方案最能解决用户痛点。</think>在 Hive on Spark 中优化数据倾斜问题,需要结合配置调整、SQL 改写和特定算法。以下是关键优化方法: --- ### **一、分组聚合导致的数据倾斜优化**[^2][^3] 1. **Map 端预聚合** 启用 `hive.map.aggr = true`,减少 Shuffle 数据量。 2. **倾斜键分离处理** 将热点 key 单独提取处理,再合并结果: ```sql -- 1. 处理非倾斜数据 SELECT key, sum(value) FROM table WHERE key NOT IN ('hot_key1', 'hot_key2') GROUP BY key; -- 2. 单独处理倾斜 key SELECT 'hot_key1' AS key, sum(value) FROM table WHERE key = 'hot_key1' UNION ALL SELECT 'hot_key2' AS key, sum(value) FROM table WHERE key = 'hot_key2'; ``` 3. **随机数打散** 对倾斜 key 添加随机后缀,分散计算压力: ```sql SELECT split(key, '_')[0] AS key, sum(value) FROM ( SELECT concat(key, '_', cast(rand()*10 as int)) AS key, value FROM table ) tmp GROUP BY key; ``` --- ### **二、Join 操作导致的数据倾斜优化**[^2][^3][^4] 1. **Map Join 强制广播小表** 自动触发条件:小表 < `hive.auto.convert.join.noconditionaltask.size`(默认 10MB)。 手动指定: ```sql SELECT /*+ MAPJOIN(small_table) */ ... FROM big_table JOIN small_table ON ...; ``` 2. **倾斜键分离 + Map Join** 分离大表倾斜 key,小表拆分后分别 Join: ```sql -- 1. 提取大表热点 key 数据 CREATE TABLE skewed_data AS SELECT * FROM big_table WHERE key IN ('hot_key1', 'hot_key2'); -- 2. 非热点数据使用普通 Join SELECT ... FROM big_table b JOIN small_table s ON b.key = s.key WHERE b.key NOT IN ('hot_key1', 'hot_key2'); -- 3. 热点数据 + 拆分后的小表使用 Map Join SELECT /*+ MAPJOIN(sub_s) */ ... FROM skewed_data b JOIN (SELECT * FROM small_table WHERE key IN ('hot_key1','hot_key2')) sub_s ON ...; ``` 3. **Sort Merge Bucket Map Join (SMB Join)**[^2] **适用场景**:大表间 Join,且表已分桶排序。 **步骤**: ```sql -- 创建分桶排序表 CREATE TABLE bucketed_table CLUSTERED BY (key) SORTED BY (key) INTO 32 BUCKETS; -- 启用 SMB Join SET hive.optimize.bucketmapjoin = true; SET hive.optimize.bucketmapjoin.sortedmerge = true; ``` --- ### **三、关键参数配置**[^1][^2][^3] ```sql -- 通用倾斜优化 SET hive.optimize.skewjoin = true; -- 开启 Join 倾斜处理 SET hive.skewjoin.key = 100000; -- 超过此值视为倾斜 key SET hive.skewjoin.mapjoin.map.tasks = 10000; -- 处理倾斜的 Map Task 数 SET hive.skewjoin.mapjoin.min.split = 33554432; -- 最小分片大小 (32MB) -- 增加并行度 SET spark.sql.shuffle.partitions = 200; -- 调整 Shuffle 分区数 SET mapreduce.job.reduces = 100; -- 设置 Reduce 数量 -- 内存优化 (防 OOM) SET spark.executor.memory = 8g; -- Executor 堆内存 SET spark.yarn.executor.memoryOverhead = 2g; -- 堆外内存 ``` --- ### **四、其他优化技巧** 1. **小文件合并** 减少 Map Task 负载: ```sql SET hive.merge.mapfiles = true; -- Map 输出合并 SET hive.merge.sparkfiles = true; -- Spark 输出合并 SET hive.merge.size.per.task = 256000000; -- 合并文件大小 (256MB) ``` 2. **动态分区优化** 避免分区过多导致元数据压力: ```sql SET hive.exec.dynamic.partition.mode = nonstrict; SET hive.exec.max.dynamic.partitions = 10000; ``` --- ### **诊断数据倾斜方法**[^4] 1. **Spark UI 观察** - 检查 Stage 中是否有 **少数 Task 处理时间远高于其他**。 - 查看 **Shuffle Read/Write 数据量分布**,是否严重不均。 2. **日志分析** 出现 `ExecutorLostFailure` 或 `OOM` 错误时,大概率是倾斜导致。 --- ### **示例:Join 倾斜优化前后对比** **原始 SQL** (易倾斜): ```sql SELECT a.*, b.* FROM big_table a JOIN small_table b ON a.key = b.key; ``` **优化后 SQL** (分离热点 key): ```sql -- 提取热点 key 列表 (如出现频率 > 10万次) CREATE TABLE hot_keys AS SELECT key FROM big_table GROUP BY key HAVING count(1) > 100000; -- 非热点数据 Join SELECT a.*, b.* FROM big_table a JOIN small_table b ON a.key = b.key WHERE a.key NOT IN (SELECT key FROM hot_keys) UNION ALL -- 热点数据 + Map Join SELECT /*+ MAPJOIN(b) */ a.*, b.* FROM big_table a JOIN small_table b ON a.key = b.key WHERE a.key IN (SELECT key FROM hot_keys); ``` ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值