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 本章知识体系图
下面这张图,把存储选型、文件格式、分区桶策略串在了一起。你可以把它当作一个决策框架:
这张图的核心逻辑是:先选存储(对象存储 vs HDFS),再定格式(Parquet/ORC/Avro),最后设计分区和桶策略。每一步都影响后续的性能和成本,千万别跳着来。
最后说一句:存储选型没有银弹。我见过用 HDFS 跑得飞起的团队,也见过被 S3 延迟坑惨的项目。关键是要理解自己的业务场景,然后做出权衡。嗯,这部分内容就到这里,希望对你有帮助。