Hadoop 小文件太多怎么处理、数据倾斜怎么解决?干了 8 年大数据,说点得罪人的实话

🔑 关键词:Hadoop小文件处理,数据倾斜解决方案,数据中台,Spark调优,HDFS NameNode内存

📖 摘要:一个干了 8 年大数据的工程师,从 NameNode 被 1.8 亿个小文件撑爆、Spark 卡在 99% 两小时的真实事故讲起,聊聊小文件、数据倾斜、数据中台这几件被讲烂了但没人讲透的事。带具体参数、真实踩坑和一点个人偏见。

先从一个被撑爆的 NameNode 说起

图片

2021 年冬天,我在一家做电商的公司,负责离线集群。那年双十一前两周,运维在群里甩了一张截图:NameNode 堆内存 32G,GC 时间占比 47%,一天告警 300 多次。我当时刚泡好一杯速溶咖啡,喝了一口,凉的。

问题查出来不复杂,就是小文件。我们把 fsimage 拉下来统计了一下:1.8 亿个文件(包括目录和 block)。Hadoop 官方那篇《The Small Files Problem》里说得很清楚,每个文件、目录、块在 NameNode 内存里大约占 150 字节。1.8 亿 × 150 字节 ≈ 27GB,这还只是元数据,没算 Hive 的 lock 表、临时目录、.staging 残留。集群是双 NameNode HA,两个节点都在 OOM 边缘反复横跳。

具体症状是这样的:跑一条 select count(*) from ods_page_log where dt='2021-11-09',卡在 "Submitting application to ResourceManager" 这一步 20 分钟。不是 SQL 慢,是 NameNode 每次 getFileInfo 都要排队。那天我盯着终端里的光标闪了 20 分钟,眼皮开始跳,一抽一抽的,到现在偶尔还这样。

怎么解决的?三条路,我们全走了:

图片

  1. 入口合并。写入 Hive 的时候开 hive.merge.mapfiles=true、hive.merge.mapredfiles=true,把 hive.merge.smallfiles.avgsize 从默认的 16MB 调到 128MB,hive.merge.size.per.task 保持默认 256MB(很多人不知道这个值默认是 256000000 字节)。这一条下去,日增文件数从 40 万降到 6 万。
  2. 存量归档。用 Hadoop Archive(HAR)把 90 天前的分区打成一个 .har 文件,单个文件最多能塞 1000 个原文件。但说实话,HAR 的读取性能会掉 20%-30%,后来我们改成直接转 Iceberg。
  3. 治本。把上游 Flink 任务的小批量 checkpoint 从 1 分钟改成 10 分钟,别一个个小文件往 HDFS 里扔。

这一段我想说的是:小文件不是什么高级问题,但它能把你整个集群拖死。很多人一上来就学 Flink、学实时数仓,结果连 HDFS 的 block 是怎么写进去的都没搞明白。

数据倾斜:那个让 reduce 卡在 99% 的夜晚

同一家公司,第二年春天。一个 GMV 汇总任务,Spark 跑了 2 小时 40 分钟,其中 2 小时 15 分钟卡在最后 1% 的 reduce 上。我去看 Spark UI,某个 task 处理了 4.7 GB shuffle 数据,其他 task 只有 30MB。差了 150 倍。

原因土得掉渣:埋点表里的 user_id 字段,有 68% 的行是 null 或者字符串 "unknown"(安卓端 SDK 没拿到设备号的时候会写这个)。这些全被 hash 到同一个分区里去了。

图片

标准解法是加盐打散 + 两阶段聚合:先给 key 拼一个 0-9 的随机前缀做局部聚合,再去掉前缀做全局聚合。代码网上到处都有,我不重复了。我真正想说的是两个更省事的办法:

  • 开 AQE。Spark 3.2 开始,spark.sql.adaptive.enabled 已经默认是 true(3.0 到 3.1 默认还是 false,这个坑我踩过)。它可以自动把倾斜的分区再切开,很多时候不用你手写加盐。但注意 spark.sql.adaptive.skewJoin.enabled 也需要打开,默认是 true 但有些发行版会关掉。
  • 把 null 提前过滤掉。听起来很蠢,但真的有效。我们在 ODS 层直接 where user_id is not null and user_id != 'unknown',那一个任务从 2 小时 40 分降到 38 分钟。

那天晚上我在工位上趴着睡了一觉,醒来的时候脖子右侧僵得转不过去,缓了三天。所以现在有人问我"学大数据累不累",我都说累的不是脑子,是颈椎。

说点得罪人的:数据中台这事儿

图片

2019 年阿里带火数据中台,2020 到 2021 年我见过至少七八家公司砸钱建,少的三五百万,多的一两千万。我参与过其中一个,预算 800 万,两个数据团队加一个外包供应商,干了 14 个月。

交付的时候是什么样?12 张所谓的"全域宽表",一套指标平台,上面挂着 340 个指标,日活用户——我印象里是 11 个,其中 6 个是数据团队自己为了测试点的。

2023 年阿里自己搞"1+6+N"组织变革,把中台拆了(这事儿是公开的,张勇当年发的全员信里写了)。我不觉得打脸,我觉得这是必然。数据中台从来不是技术问题,是组织问题。 当业务部门各算各的 GMV、各定各的"活跃用户"口径,你建多少个中台都统一不了;当 CEO 愿意拍板说"以后全公司只认一个口径",你就算只有三张 Hive 表也能当中台用。

这是我的偏见,可能很多人不同意。但我在上一家公司见过最有效的一次"数据治理",是业务 VP 在周会上骂了一句"谁再报一个不一样的日活数字,这个月绩效打 C"。就这么一句,比我们做了半年的数据资产盘点管用得多。

图片

选型那些事:别被 PPT 带跑

现在国内做 OLAP 的,绕不开 ClickHouse、Doris、StarRocks 三个。我简单说人话:

  • ClickHouse:单表扫全量是真的快,我们一张 80 亿行的明细表,简单聚合查询 1-2 秒。但它的 join 是真的弱,别硬做多表关联,会死得很难看。还有,它的删除和更新是异步 mutation,你要是指望它做实时更新,会疯。
  • Doris / StarRocks:join 和并发比 CK 友好太多,适合做 BI 报表。我们现在的看板是 StarRocks 撑的,QPS 300 左右,P99 大概 800ms。但数据量上到几千亿行,就得靠分区裁剪和物化视图救命了。

我不太喜欢"哪个更好"这种问法。我一般回问一句:你的查询是宽表点查还是多表关联,你的数据是更新还是追加,你的并发是 10 还是 1000。 答完这三个问题,选型基本就定了。

给刚入行的人一个自查清单

图片

如果你现在在做大数据,我建议你对着这几条自查,能答上来大部分人算入门了:

  1. HDFS 一个 block 默认多大?为什么不是随便设?(128MB,Hadoop 2.x 起;1.x 是 64MB。设太大浪费小文件场景的并行度,设太小 NameNode 元数据扛不住)
  2. 你的 Spark 任务 shuffle 分区数是多少?spark.sql.shuffle.partitions 默认是 200,10GB 数据跑 200 个分区,每个分区 50MB,网络 IO 和 GC 都会很难受。我一般按 数据量 / 200MB 估。
  3. YARN 报 Container killed by YARN for exceeding memory limits. 10.7 GB of 10 GB physical memory used 的时候,你先调哪个参数?(先看 spark.yarn.executor.memoryOverhead,默认是 executor 内存的 10%,一般调到 15%-20%)
  4. 你的任务挂了,能一键重跑吗?重跑会重复写数据吗?

最后一条其实是最重要的。我干了这么多年,越来越觉得大数据的门槛不在算法,在"能不能自动重跑"和"挂了之后数据对不对"。所谓数据驱动,很多时候就是老板 PPT 上需要那个数字时,你半夜十二点能跑出来。

写完这些,凌晨一点多了。窗外不知道谁家还在放歌。就到这里吧。

🏷️ 标签: