1. Compaction基础概念:小文件问题从何而来

各位同学好,我是老张。今天咱们正式开讲数据湖仓的Compaction调优。第一节课,先把地基打牢——到底什么是Compaction?为什么每个做湖仓的人早晚都得面对它?

我2018年开始接触Delta Lake,那时候团队刚把离线数仓往数据湖上迁移。跑了不到两个月,发现一个怪现象:查询越来越慢,元数据操作动不动就超时。一查HDFS,好家伙,一个分区里躺着几千个小文件。这就是我们今天要聊的核心——小文件问题。

1.1 什么是数据湖仓的Compaction

Compaction,直译过来叫“压缩”或“合并”。但在数据湖仓里,它特指把小文件合并成大文件,同时清理掉冗余数据的过程。

你想想看,数据湖仓底层通常是HDFS、S3这类对象存储或分布式文件系统。它们擅长存大文件,不擅长处理海量小文件。Compaction就是干这个活的——把零散的小碎片拼成规整的大块。

核心定义:Compaction = 小文件合并 + 数据清理 + 元数据优化。三者缺一不可。

我用一个简单的例子说明。假设你有一个订单表,每分钟写入一批数据。一天下来,产生了1440个小文件。Compaction会把这些文件合并成几个大文件,比如按小时合并成24个,甚至按天合并成1个。

-- 合并前:1440个小文件,每个几KB到几MB
/orders/dt=2024-01-01/part-00001-xxx.snappy.parquet
/orders/dt=2024-01-01/part-00002-xxx.snappy.parquet
...
/orders/dt=2024-01-01/part-01440-xxx.snappy.parquet

-- 合并后:1个大文件,约几百MB
/orders/dt=2024-01-01/compacted-00001-xxx.snappy.parquet

1.2 为什么需要Compaction

说白了,不做Compaction,你的数据湖仓会慢慢“卡死”。我见过太多团队,前期只顾着往湖里灌数据,不管文件治理。三个月后,查询慢得像蜗牛,运维同学天天加班。

具体来说,Compaction解决了三个层面的问题:

层面 问题 Compaction的作用
查询性能 小文件过多导致Task数爆炸,调度开销大 减少文件数,降低调度和I/O开销
元数据管理 Hive Metastore/Catalog中条目膨胀 合并后元数据条目减少,DDL操作更快
存储效率 小文件浪费块空间,压缩率低 大文件压缩比更高,节省存储成本

我记得有一次,一个业务方反馈说“查询一个月的订单数据要跑40分钟”。我上去一看,那个分区有2万多个小文件。跑了一次Compaction,文件数降到200个,查询时间直接降到4分钟。十倍提升,就这么简单。

我的经验:Compaction不是锦上添花,而是数据湖仓的“日常保洁”。不做保洁的房子,住久了肯定出问题。

1.3 小文件问题的由来

小文件不是凭空冒出来的。它的产生,跟数据湖仓的写入模式密切相关。我总结了几个最常见的“元凶”:

1.3.1 流式写入的天然缺陷

实时流处理(比如Spark Streaming、Flink)每批次都会生成新文件。默认情况下,批次间隔越短,文件越小。举个例子:

-- Spark Structured Streaming 配置
spark.sql.streaming.schemaInference = true
-- 每5秒一个批次,一天产生17280个微批次文件
-- 每个文件可能只有几百KB

为什么会这样?因为流处理引擎为了低延迟,会尽快把数据刷出去。它不管文件大小,只管“这批数据写完了没”。

1.3.2 分区策略不当

很多同学喜欢按“小时”甚至“分钟”分区。分区越细,每个分区内的数据量越少,文件自然就小了。

-- 错误示范:过度分区
PARTITIONED BY (year, month, day, hour, minute)
-- 每分钟一个分区,每个分区可能只有几条数据

我见过最夸张的一个案例:某团队按“年/月/日/小时/分钟”五级分区,一个分区里就三五条记录。这种设计,神仙来了也救不了。

1.3.3 并发写入冲突

数据湖仓支持多并发写入,这是好事。但每个写入任务都会生成自己的文件。并发数越高,文件数越多。

-- 假设100个并发写入同一个分区
-- 每个写入生成1个文件,就是100个小文件
-- 如果每个写入还分多个task,文件数翻倍

1.3.4 更新删除操作

湖仓的ACID特性(比如Delta Lake的MERGE、UPDATE)会生成新的数据文件,同时保留旧版本。久而久之,文件数量只增不减。

避坑指南:我曾经接手过一个项目,MERGE操作每天执行上百次,每次生成几十个小文件。三个月后,文件数突破百万。元数据操作直接超时。后来加了Compaction策略,才把局面稳住。

1.4 小文件问题的量化影响

光说“小文件不好”不够直观。咱们用数据说话:

指标 100个小文件(每个10MB) 1个大文件(1GB) 差异
查询启动时间 ~15秒(调度+元数据) ~2秒 7.5倍
实际扫描时间 ~30秒(I/O开销) ~8秒 3.75倍
总查询时间 ~45秒 ~10秒 4.5倍
存储空间 ~1.2GB(含块浪费) ~0.9GB(压缩率高) 节省25%

你看,小文件不仅拖慢查询,还浪费存储。这笔账,怎么算都不划算。

1.5 本章知识体系总览

为了让大家对Compaction有个整体认知,我画了一张图,把核心概念串起来:

Compaction 核心知识体系 Compaction 为什么需要? 查询性能下降 元数据膨胀 存储效率低 小文件来源 流式写入(微批次) 分区策略不当 并发写入冲突 更新/删除操作 Compaction 核心动作 小文件合并 数据清理 元数据优化

这张图把今天的内容串起来了。左边是“为什么需要Compaction”,右边是“小文件从哪来”,底部是“Compaction具体做什么”。后面的课程,我们会逐一深入每个环节。

1.6 小结

今天咱们聊了Compaction的基础概念。记住三句话:

  • Compaction就是合并小文件、清理冗余、优化元数据
  • 不做Compaction,查询会越来越慢,存储越来越浪费
  • 小文件主要来自流式写入、过度分区、并发冲突和更新删除