数据存储与分区:从原始数据到可分析资产

大家好,欢迎来到第二章。上一章我们聊了全链路管理的整体视图,今天要扎进一个非常具体、也非常关键的话题——数据存储与分区。

说实话,我见过太多团队在模型训练上花了大把精力,结果发现数据准备阶段就埋了雷。要么存储选型不对,跑个查询要等半天;要么分区策略混乱,数据工程师天天在修分区边界。嗯,这些坑我都踩过。今天咱们就把这块掰开揉碎了讲清楚。

数据湖 vs 数据仓库:别再傻傻分不清

先问个问题:你手头的数据,到底该放湖里还是放仓里?

很多人觉得这俩概念差不多,其实差别大了去了。我个人的理解是——数据湖存的是"原材料",数据仓库存的是"半成品"。

维度 数据湖 数据仓库
数据格式 原始格式(CSV、JSON、日志、图片) 结构化/半结构化(Parquet、ORC)
Schema 读时模式(Schema-on-Read) 写时模式(Schema-on-Write)
主要用途 探索、数据科学、机器学习 BI报表、固定查询、业务分析
数据质量 较低,允许脏数据 较高,经过清洗转换
典型技术 HDFS、S3、ADLS、MinIO ClickHouse、Snowflake、Redshift

我在一个电商项目中遇到过这样的情况:业务方想分析用户行为路径,数据仓库里只有聚合后的订单表,根本没法做序列分析。后来我们把原始点击日志直接丢进数据湖,用Spark跑一遍,问题就解决了。说白了,数据湖给了你"后悔"的权利——原始数据还在,随时可以重新加工。

核心原则:数据湖存"原样",数据仓库存"有用"。两者不是替代关系,是上下游关系。

HDFS vs 对象存储:选型背后的门道

聊完湖和仓,接下来就是底层存储选型了。目前主流就两派:HDFS 和对象存储(S3、OSS、MinIO 之类的)。

我记得刚入行那会儿,HDFS 几乎是唯一选择。但近几年对象存储越来越火,甚至有些团队直接把 HDFS 换掉了。为什么会这样?

HDFS 的优势与局限

  • 强一致性:写入后立即可见,适合需要严格一致性的场景
  • 本地计算亲和性:数据本地化调度,减少网络开销
  • 但……扩展性受限,NameNode 是单点瓶颈;运维成本高,集群挂了恢复很痛苦

对象存储的优势与局限

  • 无限扩展:理论上存储容量无上限,按量付费
  • 高可用:多副本、跨区域复制,基本不用操心运维
  • 但……最终一致性(某些场景下会有延迟);计算和存储分离,网络开销大

我的建议:如果团队规模小、运维能力弱,优先选对象存储。如果对延迟和一致性要求极高,或者有大量本地计算任务,HDFS 仍然有它的位置。我在一个广告推荐项目中用过 S3 + Spark 的组合,效果不错,但要注意调整 shuffle 策略来减少网络传输。

分区策略:时间分区 vs 业务分区

存储选型定了,接下来就是怎么组织数据。分区策略做得好,查询效率能提升一个数量级。做不好……嗯,我曾经见过一个表有 2000 多个分区,每次查询都要扫全表。

分区策略主要分两种:时间分区和业务分区。实际项目中通常是两者结合。

时间分区

按日期、小时甚至分钟来划分数据。这是最常用的方式,因为大部分数据都有时间属性。

-- 按天分区示例(Hive SQL)
CREATE TABLE user_behavior (
    user_id STRING,
    action STRING,
    page_url STRING
)
PARTITIONED BY (dt STRING)
STORED AS PARQUET;

-- 查询某天的数据
SELECT * FROM user_behavior WHERE dt = '2024-03-15';

时间分区的粒度怎么定?我个人的经验是:日分区是默认选择。小时分区适合数据量极大且查询频繁的场景,但分区数会膨胀得很快。分钟级分区?除非你有特殊需求,否则别碰。

业务分区

按业务维度划分,比如地域、产品线、用户等级等。

-- 业务分区示例
CREATE TABLE sales_data (
    order_id STRING,
    amount DOUBLE,
    product_id STRING
)
PARTITIONED BY (region STRING, year STRING)
STORED AS ORC;

业务分区有个坑:分区键的选择要谨慎。我曾经有个项目,按"城市"分了 300 多个分区,结果小城市的数据只有几条记录,查询时反而增加了元数据开销。后来改成了按"省份"分区,效果好了很多。

避坑指南:分区不是越多越好。每个分区都会产生元数据,分区数超过 10000 后,NameNode 或对象存储的元数据服务会承受巨大压力。我曾经见过一个极端案例——分区数超过 5 万,查询计划生成就要花 30 秒。

列式存储:Parquet 与 ORC 的较量

最后聊聊文件格式。如果你还在用 CSV 或 JSON 存分析数据,我建议你立刻停下来。列式存储才是数据仓库和分析场景的正确姿势。

为什么列式存储快?说白了,它只读取你需要的列。比如你有一个 100 列的表,但只查其中 3 列,列式存储只需要读那 3 列的数据。行式存储呢?得把整行都读出来,再过滤掉不需要的列。你想想看,这差距有多大。

特性 Parquet ORC
压缩率 高(支持多种编码) 更高(轻量级索引 + 字典编码)
读取性能 优秀(谓词下推支持好) 优秀(内置索引,跳过无效数据更快)
写入性能 中等 较好(批量写入优化)
生态兼容性 极广(Spark、Hive、Presto、Trino 等) 较广(Hive、Spark 支持,部分引擎需适配)
适用场景 通用分析、数据湖 Hive 重度用户、高压缩需求

我在一个日志分析项目中做过对比:同样的 10TB 数据,CSV 格式占用 12TB,换成 Parquet 后压缩到 1.8TB,查询速度提升了 8 倍。ORC 的压缩率更高,但当时我们的计算引擎对 ORC 的支持不够好,所以选了 Parquet。

选型建议:如果你的技术栈以 Spark 为主,Parquet 是更稳妥的选择。如果重度使用 Hive,ORC 的索引优势会更明显。两者都是好格式,选一个你团队最熟悉的就好。

知识体系总览

说了这么多,我画了一张图帮你把今天的内容串起来。这张图展示了从数据源到最终分析的全链路存储架构。

数据存储与分区知识体系 数据存储与分区知识体系 数据源(日志/DB/API) 存储选型 HDFS(强一致/本地计算) 对象存储(弹性/低成本) 数据组织方式 数据湖(原始/读时模式) 数据仓库(清洗/写时模式) 分区策略 时间分区(dt/hour) 业务分区(region/product) 组合分区(dt+region) 文件格式(列式存储) Parquet(生态广/通用) ORC(压缩高/索引强) 核心原则:数据湖存原始 → 分区优化查询 → 列式存储压缩加速 → 数据仓库服务分析 选型没有银弹,根据团队规模、查询模式、运维能力综合决策

这张图把今天的内容串成了一个完整链路:数据源进来后,先选存储(HDFS 或对象存储),再决定是放湖里还是仓里,然后设计分区策略,最后用列式格式落地。每一步都有取舍,没有银弹。