3、数据湖存储选型:对象存储、HDFS 与文件格式

存储选型这件事,说白了就是给数据湖找个「家」。我见过不少团队,一上来就纠结用 HDFS 还是对象存储,结果折腾半天连数据格式都没定。今天咱们就把这块掰开揉碎,从底层特性到上层策略,一次性讲清楚。

核心观点:对象存储是数据湖的默认选择,HDFS 更适合存算紧耦合的场景。文件格式选 Parquet,分区策略要结合查询模式来设计。

3.1 对象存储(S3/OSS)核心特性

对象存储,你可以把它想象成一个无限大的键值仓库。每个文件就是一个对象,通过唯一的 key 来访问。AWS S3 和阿里云 OSS 是市面上最主流的两个选择。

我个人习惯把对象存储的特性总结为三点:

  • 无限扩展:你不用担心容量上限。我管过的一个日志平台,每天写入 50TB 数据,S3 从来没报过错。
  • 强一致性:写入后立即读取,数据一定是最新的。以前 S3 是最终一致性,现在早就改了。
  • 按量付费:只用付存储费和请求费。不像 HDFS,你得先买一堆服务器。

但这里有个坑——对象存储的延迟比本地磁盘高。你想想看,每次读写都要经过网络,延迟通常在 10-50ms 级别。而 HDFS 的本地读延迟可以做到 1ms 以下。所以,如果你的业务对延迟极其敏感,比如实时风控,那对象存储就不太合适。

避坑指南:我曾经遇到过一个团队,把实时流处理的结果直接写到 S3,结果因为 S3 的写入延迟,导致下游任务频繁超时。后来我们加了 Kafka 做缓冲,才把问题解决。

3.2 HDFS 与云存储对比

HDFS 和云存储,本质上都是分布式文件系统。但它们的架构差异,决定了各自适用的场景。

对比维度 HDFS 对象存储(S3/OSS)
扩展性 受限于集群规模,扩容需要加节点 理论上无限,自动扩展
一致性 强一致性 强一致性(现代版本)
延迟 低(本地磁盘) 中(网络延迟)
成本 固定成本(服务器+运维) 可变成本(按量付费)
适用场景 存算一体,批处理为主 存算分离,数据湖首选

我个人的经验是:如果你在云上,优先选对象存储。为什么?因为存算分离是趋势。计算资源可以随时启停,存储成本却一直存在。用对象存储,你可以把计算集群在空闲时缩到零,省下的钱可不是小数目。

但如果你在自建机房,或者有严格的合规要求,HDFS 依然是个稳妥的选择。我记得有个金融客户,数据必须留在本地,他们就用 HDFS 搭了一套数据湖,跑了三年没出过大问题。

注意:HDFS 的 NameNode 是单点故障。虽然现在有 HA 方案,但运维复杂度比对象存储高不少。如果你团队不大,建议慎选。

3.3 数据湖文件格式:Parquet / ORC / Avro

文件格式选得好,查询性能差十倍。这不是夸张,是我踩过的坑。

目前主流的数据湖文件格式有三种:Parquet、ORC 和 Avro。它们各有侧重,我简单对比一下:

特性 Parquet ORC Avro
存储方式 列式 列式 行式
压缩比 高 更高 中
读取性能 列投影快 列投影快 全行扫描快
写入性能 中 中 快
Schema 演进 支持 支持 原生支持
生态兼容性 最广(Spark、Hive、Presto) Hive 为主 Kafka、Hadoop 生态

我的建议很简单:

  • 分析查询为主:选 Parquet。它生态最好,Spark、Presto、Trino 都原生支持。
  • Hive 重度用户:可以选 ORC。ORC 的压缩比确实比 Parquet 高一点,但生态不如 Parquet 广。
  • 流式写入或消息队列场景:选 Avro。行式存储写入快,而且 Schema 演进最灵活。

个人经验:我做过一个项目,一开始用 Avro 存历史数据,结果查询慢得要命。后来改成 Parquet,同样的查询从 5 分钟降到了 30 秒。所以,分析场景千万别用行式存储。

3.4 分区与桶策略设计

分区和桶,是数据湖性能优化的两个核心手段。用好了,查询效率翻倍;用不好,反而拖慢系统。

3.4.1 分区策略

分区,就是把数据按某个字段拆成不同的目录。最常见的分区字段是时间,比如按天分区:

s3://data-lake/events/dt=2025-01-01/
s3://data-lake/events/dt=2025-01-02/
s3://data-lake/events/dt=2025-01-03/

查询时,如果带上分区条件,引擎只会扫描相关目录,大大减少数据量。

但分区不是越多越好。我见过有人按小时分区,结果每个分区只有几百 KB 的文件。这样反而增加了文件数,导致元数据压力大,查询变慢。

经验之谈:分区粒度要结合数据量和查询模式。一般来说,每天数据量在 100GB 以上,按小时分区才有意义。否则,按天分区就足够了。

3.4.2 桶策略

桶(Bucket)是更细粒度的数据组织方式。它通过哈希将数据分散到固定数量的桶中,常用于解决数据倾斜问题。

举个例子,如果你按用户 ID 分区,但有些用户的数据量特别大,就会导致分区倾斜。这时候可以用桶:

-- 创建桶表
CREATE TABLE user_events (
  user_id STRING,
  event_time TIMESTAMP,
  event_type STRING
)
CLUSTERED BY (user_id) INTO 64 BUCKETS;

这样,数据会按 user_id 的哈希值均匀分布到 64 个桶中。查询时,如果带上 user_id 条件,引擎只需要扫描对应的桶。

我个人习惯把桶数设为 2 的幂次,比如 16、32、64、128。这样哈希分布更均匀,而且方便后续调整。

3.4.3 分区 vs 桶:怎么选?

维度 分区 桶
粒度 粗(目录级别) 细(文件级别)
适用场景 时间范围查询 等值查询、Join 优化
数据倾斜 容易产生 能缓解
管理成本 低 中

我的建议是:先分区,再桶。分区解决大范围的裁剪问题,桶解决小范围的均匀分布问题。两者结合,效果最好。

实战案例:我曾经优化过一个广告点击分析系统。原始数据按天分区,但查询经常按广告主 ID 过滤。加了桶之后,查询性能提升了 3 倍。关键是,桶数要跟查询的并发度匹配,不然效果会打折扣。

3.5 本章知识体系图

下面这张图,把存储选型、文件格式、分区桶策略串在了一起。你可以把它当作一个决策框架:

数据湖存储选型知识体系 数据湖存储选型决策框架 存储选型 对象存储 (S3/OSS) HDFS 无限扩展 按量付费 低延迟 存算一体 文件格式 Parquet(列式,分析首选) ORC(列式,Hive 优化) Avro(行式,流式写入) 分区 + 桶策略 分区:按时间裁剪数据 桶:哈希均匀分布 先分区,再桶 分区粒度要合理

这张图的核心逻辑是:先选存储(对象存储 vs HDFS),再定格式(Parquet/ORC/Avro),最后设计分区和桶策略。每一步都影响后续的性能和成本,千万别跳着来。

最后说一句:存储选型没有银弹。我见过用 HDFS 跑得飞起的团队,也见过被 S3 延迟坑惨的项目。关键是要理解自己的业务场景,然后做出权衡。嗯,这部分内容就到这里,希望对你有帮助。