数据治理的事情,聊到存储这一章,我猜不少人是又爱又恨。做数据开发这几年,我见过太多团队在一开始把精力全扑在采集、清洗、建模上,觉得存储无非就是买点磁盘、搭个集群,结果数据量一上来,跑批慢、费用飙、找数难,全都在存储这个环节爆雷。这套“数据治理概论”连载到第5章,总共123页的内容里,第5章专门拆数据存储,讲得很系统,但很多点落在实际工作中还得有人再补一层“怎么用”的细节。这篇就把我基于这一章梳理出来的思路、选型逻辑、踩坑记录一起放出来,给正在做数据开发、数据治理或者准备面试的同学做个参考。
存储这个东西,听起来像基础设施,其实是数据治理里最容易“欠债”的地方。一张表用什么文件格式存、分区怎么切、生命周期怎么设、元数据怎么管,任何一个决策都会在三个月后变成成本账单或者故障工单。所以这一章不光是讲“怎么存”,更是在讲“怎么用最小的代价把数据管住”。
1. 数据存储为什么是数据治理的地基 —— 项目定位与整体思路
1.1 从数据生命周期看存储的位置
数据治理通常讲采集、存储、处理、分析、归档、销毁几个阶段,但很少有人意识到,存储其实是贯穿全程的那根线。采集进来的原始数据要落地存储,清洗后的中间结果要存储,最终的报表、特征、标签也要存储。存储一旦乱了,前面采集做得再干净,后面分析也用不起来。
我之前接触过一家企业的数仓团队,他们每天凌晨跑批,任务经常失败,排查下来发现不是SQL写错了,而是底层的存储目录权限混乱,A团队写的临时表被B团队的清理任务误删了。这就是典型的存储治理缺失。数据治理概论第5章在讲存储时,其实隐含了一个核心逻辑:存储治理不是单独的环节,而是给上下游立规矩。规矩立好了,数据才能谈得上“资产”,否则就是一堆占着磁盘的字节。
1.2 数据存储治理要解决的三大矛盾
第5章里并没有直接列这个,但我看完之后总结了三个绕不开的矛盾,做存储治理本质上就是在它们之间找平衡。
第一个是成本与性能的矛盾。全上SSD、全放热存储,查询确实快,但费用成倍涨;全用冷存储、压缩算法拉满,便宜是便宜,跑一次分析要等半天。现实做法是做分层,但分层策略怎么定、数据多久从热层降到温层再降到冷层,这就要靠治理规则来约束。
第二个是效率与规范的矛盾。开发人员最怕流程繁琐,希望建个表就能写数。但从治理角度,表命名、目录结构、格式规范、字段注释都得有标准。标准太松,后面全是坑;标准太严,开发天天骂娘。一个好的存储治理方案,要把规范尽量内嵌到工具链里,让守规矩变成最省事的选择。
第三个是复用与安全的矛盾。数据要共享给多个团队用,才能产生更大价值,但存储层如果不去控制权限、不去脱敏,敏感数据就裸奔。很多公司出事不是因为没做安全制度,而是存储层面缺乏细粒度的隔离和审计。
如果你们团队正在搞数据治理,先别急着上平台、上工具,把存储这三大矛盾梳理清楚,再谈选型,心里就有底了。
2. 存储选型:先定格式,再定引擎
2.1 结构化、半结构化、非结构化数据的存储格式选择
数据存储格式这个话题,面试几乎必考,实际项目里也最容易出问题。很多开发对格式的理解停留在“能用就行”,实际上格式决定了压缩率、查询性能、Schema演进的灵活性,是存储治理的第一道分水岭。
结构化数据,比如订单表、用户表,有明确的Schema,行式存储和列式存储差别很大。OLTP场景,像MySQL这类行式存储,按主键查单条数据快;OLAP场景,像Hive里的ORC、Parquet这类列式存储,只读需要的列,I/O大幅减少。这就是为什么同样的数据量,用Parquet跑分析比用TextFile快好几倍。
半结构化数据,比如JSON、XML、日志,常见的存储思路有几种:直接以文本形式存在对象存储里,或者用MongoDB这类文档数据库。关键在于解析成本。如果数据要频繁被SQL查询,最好在入湖时转换成结构化格式;如果只是存档备查,保留原始JSON反而更灵活。
非结构化数据,图片、音视频、文档,一般是对象存储的天下。存储治理在非结构化数据上的重点是目录规划、生命周期策略和敏感内容识别,格式本身一般不做强制转换。
选格式有个经验法则:大数据分析场景优先列式存储,小数据服务场景优先行式存储,有嵌套结构且Schema不稳定的,优先Parquet或Avro,而不是强行拍平。
2.2 常见存储引擎选型对照
第5章花了挺大篇幅讲数据仓库、数据湖、湖仓一体,但很多概念没有落到选型对比上。我把常见的几类存储引擎放在一张表里,方便你对照思考。
| 存储类型 | 代表性方案 | 最适合的场景 | 必须注意的坑 |
|---|---|---|---|
| 关系型数据库 | MySQL、PostgreSQL | 事务型业务系统、在线交易 | 别拿它硬扛分析查询,数据量大后索引和锁都是问题 |
| 数据仓库 | Hive、Snowflake、Doris、ClickHouse | 大规模结构化数据的离线分析、报表 | 建表不规范会导致元数据混乱,分区剪裁失效 |
| 数据湖 | Hadoop HDFS、Delta Lake、Iceberg、Hudi | 多源异构数据的原始存储、机器学习训练 | 缺少元数据管理就会退化成“数据沼泽” |
| 搜索引擎 | Elasticsearch | 日志检索、全文搜索、OLAP轻量分析 | 写入吞吐有限,别把它当主存储 |
| 对象存储 | MinIO、AWS S3、阿里云OSS | 文件、图片、备份、海量非结构化数据 | 小文件过多会拖垮NameNode或者桶性能 |
很多团队做数据治理时有个误区:以为上一套数据湖就什么问题都解决了。实际上数据湖只是把存储成本和灵活性做了优化,如果格式、目录、权限、元数据不治理,湖里全是垃圾数据,后期找数、入仓、建模都会非常痛苦。
数据仓库和数据湖的边界,这两年因为湖仓一体的概念变得模糊了。以Iceberg、Hudi为代表的表格式存储层,能在数据湖上提供ACID、upsert和Schema演进能力,这正好是数据治理需要的。如果你从零开始搭平台,我建议直接考虑湖仓一体架构,省得以后把数据从湖里搬到仓里再来一次。
2.3 存储分区、分桶与文件格式的治理要点
这一小节是实操重点。分区和分桶如果设计不好,你怎么调优SQL都白搭。
分区(Partition):一般按日期、地区等低基数字段切分。好处是查询时能直接跳过无关分区,跑批任务也天然支持增量处理。坏处是分区字段选错会导致严重的数据倾斜。最常见的问题是把高基数字段(比如用户ID)当成分区键,结果一个分区一个文件,凑出几十万个小文件。正确做法是:低频固定维度(日期、月份、业务线)做分区,高基数字段做分桶或者直接不分区。
分桶(Bucket):按某个字段的哈希值将数据分散到固定数量的文件中。这样做的好处是对桶字段的join和聚合能本地优化,还能有效控制单个文件的大小。建议分桶键选择join频繁的字段,桶的数量根据数据量估算,目标让每个桶文件在256MB到1GB之间,这样既不会因为文件太小产生过多元数据,也不会因为文件太大导致并行度不足。
文件格式:Hive表常见的是Parquet、ORC、Avro,各有侧重。Parquet压缩率和查询性能综合表现好,生态支持最广;ORC在Hive原生环境下表现更好,还内置了轻量索引;Avro适合写密集和Schema演进的场景,但查询性能一般。我自己在数仓里默认用Parquet,只有在Listener日志或者CDC数据流里才用Avro。
关于小文件问题,后面第5章专门有实战排查,这里先记住一个原则:小文件的判断标准不是绝对大小,而是和块大小(比如128MB)的比值。一个几KB的文件在HDFS里就会占一块内存元数据,上百万个小文件会让NameNode直接告急。治理手段无非是三个:减少生成源头(比如合理设置spark.sql.shuffle.partitions)、定时合并(执行INSERT OVERWRITE重写)、用存储服务的自动小文件优化功能。
3. 数据存储治理的落地实操:从元数据到生命周期
3.1 元数据管理与数据字典
数据存储治理的第一步,不是买存储,而是管好元数据。什么是元数据?简单说就是“数据的数据”——这张表叫什么、字段是什么含义、从哪来、谁在用、更新频率是多少、血统关系是什么。
我将元数据分为三层:
- 技术元数据:库名、表名、字段名、类型、分区信息、文件格式、压缩算法、大小、行数。这些是系统自动生成的,关键是要采集完整,并且能可视化。
- 业务元数据:字段的业务含义、指标口径、枚举值含义、负责人。这是最容易缺失的。比如一个字段叫“amt”,到底是含税金额还是不含税金额?不写清楚,后面的分析人员全靠猜。
- 管理元数据:数据等级(内部/机密)、所属域、访问权限、保留期限、质量规则。
我在做治理项目时,会先拉一张“元数据盘点表”,把每个库的每个表都过一遍,标记缺失项。你会发现,跑了一年的大宽表,居然有40%的字段没有注释。数据字典如果没有在开发阶段强制管理,后面想补几乎是噩梦。所以我的习惯是:建表语句里必须有COMMENT,字段必须有COMMENT,缺一个就不允许上线。
元数据管理工具上,开源可以用Apache Atlas、DataHub,商业产品更多。但工具只是载体,核心是把采集、变更通知、血缘解析这几条链路跑通。没有工具的时候,用Excel维护一份数据字典也比没有强,关键是有人对它的准确性负责。
3.2 存储分层与生命周期策略
存储分层是控制成本的核心手段。数据从产生到消亡,访问频率会发生明显变化:刚接入的订单数据每天被查,三个月后可能每周只查一次,一年后可能只会被审计翻出来。如果不做分层,所有数据都按照最高标准存在SSD或者高性能存储上,成本会失控。
我习惯把数据分成三态:
- 热数据:最近7到30天内高频访问的数据,存放高性能存储或SSD,副本数保持默认(3副本),且常驻内存/缓存。
- 温数据:访问频率降低但仍需支持常规分析,可以放到标准存储,压缩算法可以适当提高压缩比。
- 冷数据:超过一定时限、很少访问,但需要满足合规保留要求,放到冷存储/归档存储,副本数可以降到2甚至1,允许一定的读取延迟。
分层策略要用代码实现,不能靠人来判断。比如可以基于表的last_access_time、表大小、更新频率,自动生成降级任务。大多数云厂商的对象存储都支持生命周期规则,你可以直接在Bucket策略里配置“30天后转低频,90天后转归档”。HDFS场景可以用存储策略命令,把目录设置成冷热目录。
生命周期管理还有一个容易忽略的点:删除策略。不能只想着存,还要明确什么时候销毁。比如日志数据保留180天,业务明细保留3年,超出时间自动清理。如果没有这个策略,数据只增不减,成本迟早爆掉。
3.3 压缩、加密与脱敏的存储侧实现
存储治理不能只盯着格式和生命周期,还得考虑效率和合规。
压缩是省成本的第一招。Parquet本身支持snappy、gzip、zstd等压缩算法。snappy压缩速度快,压缩比一般;zstd压缩比高,速度也能接受;gzip压缩比最高但CPU开销大。我个人的经验是:日常分析表用snappy,追求压缩率且不常查询的归档数据用zstd或gzip。需要注意的是,不要对同一个文件做二次压缩(比如Parquet内部压缩后,外层再用gzip压一遍),这样不仅不会显著减小体积,还会让查询无法利用列式存储的特性。
加密分为静态加密和传输加密。HDFS、S3、MySQL都支持透明加密,基本不改变上层逻辑。但密钥管理要规范,千万别把密钥写在配置文件里。云上推荐用KMS托管,自建可以用Vault。加密会影响压缩比和性能,所以加密范围要分级:核心敏感字段加密,普通数据用存储层加密即可。
脱敏一般在应用层做,但存储侧也要有配合。比如在原始数据入湖时,就把身份证、手机号等敏感信息做哈希或加盐处理,这样下游拿到的就不是明文了。另一种方式是在存储层做动态脱敏,比如用户查询时,根据权限返回掩码后的数据。这块如果做得粗,会直接影响安全合规审查。
我在实际项目中踩过的坑是:脱敏规则只在前端展示层做了,结果数据文件导出去之后还是明文。所以我现在要求:凡是导出、复制、同步的数据,必须在存储层或任务链路层做二次校验兜底,前端脱敏不等于数据安全。
4. 数据开发与治理工程师面试中存储相关问题
4.1 面试官最常问的存储类问题
结合标题热搜词里的“数据开发与治理工程师面试问题”,我估计不少读者是冲着面试准备来的。存储方向的问题,面试官最爱问这么几类:
第一类是概念对比题:“数据仓库和数据湖的区别”“Parquet和ORC的区别”“列式存储和行式存储的区别”。回答时不要只背定义,要结合场景。比如问行式存储和列式存储,你可以说:如果我的查询总是SELECT *,行式存储更友好;如果我的分析是针对特定列做聚合,列式存储能大幅减少IO。这样面试官会认为你是真用过。
第二类是架构设计题:“给你每天上亿条日志,你会怎么设计存储方案”。回答时要从接入、分层、格式、分区、生命周期、查询需求几个维度展开。比如:日志先写入Kafka,由Flink或Spark落Lake,按天分区、按小时分桶,源文件用Parquet + zstd压缩,热数据存SSD,过期自动归档到对象存储。同时要考虑元数据注册和小文件控制。
第三类是问题排查题:“数据查询突然变慢,你怎么排查”。这题表面上在考性能排查,实际在考你对存储原理的理解。回答顺序:先确认是计算瓶颈还是IO瓶颈,再查是否有小文件过多或者数据倾斜,再查分区剪裁是否生效、谓词下推是否失效,最后看是否因为文件格式改变或压缩算法导致扫描开销变大。
第四类是治理落地题:“如何管理一个混乱的存储环境”。回答重点放在元数据补齐、目录规范、权限收敛、生命周期自动化。这类题没有标准答案,但要从“能落地”而非“能用多牛的技术”来回答。
4.2 实战场景:一张订单表从设计到存储治理的完整做法
我拿一个真实场景来走一遍,帮大家把前几节的东西串起来。
假设有一张电商订单表,数据量每天新增5000万行,上游有多个业务库通过CDC同步,下游有10个分析任务依赖。这张表如果随手建,基本就是灾难。我会这样设计:
- 存储引擎:数据量级和场景属于分析型+近实时更新,选Iceberg表格式,底层用Parquet文件放在对象存储上。
- 分区策略:按业务日期(day)分区,每天一个分区。如果要更细粒度,按小时分区,但小时分区可能太小,综合考虑按天分区再加上dt字段过滤。
- 分桶策略:按user_id哈希分桶,桶数设为64,这样可以确保后续按用户维度的join和聚合在本地完成,同时避免一个桶文件过大。
- 主键与更新:订单有唯一的order_id,且可能存在更新,用Iceberg的upsert能力,通过时区+业务主键做merge。
- 压缩:用zstd压缩级别3,兼顾写入速度和压缩比,目标压缩后每行大约降低60%体积。
- 生命周期:订单表必须保留3年。为了控制成本,热分区只保留最近30天;30天到180天的分区自动转入低频存储,同时把副本数降到2;180天以上转入归档,并配置自动清理过期分区。
- 元数据:在注册元数据时必须填写字段注释、保留策略、数据owner、敏感级别。order_id、user_id、phone等敏感字段做标识,实现下游脱敏自动生效。
这套做完之后,运维成本能降低不少:省去了每天手动清理的脚本,查询也不担心全表扫描,找表的时候元数据信息一目了然。
4.3 自查清单:你的存储治理做得到不到位
面试题和实战场景都聊了,最后给一个自查清单。你可以拿它来评估当前团队或者自己负责项目的存储治理水平。
- 每张表都有明确的owner和字段注释?
- 建表时是否指定了文件格式、压缩算法、分区策略?
- 是否定期统计小文件数量和大小分布?有没有自动合并策略?
- 数据从热存储到冷存储的迁移是自动的,还是靠人肉脚本?
- 敏感数据是否做了列级加密或脱敏,而不仅是前端展示层处理?
- 权限模型是否细化到字段级别?是否做了操作审计?
- 数据删除时是否有保留策略约束,不会误删和泄露?
- 跨团队协作时,存储目录和表命名是否遵循统一规范?
- 重跑历史分区时,是否会对下游造成脏数据污染?有没有存储快照或版本回溯机制?
- 有没有一份完整的存储成本核算表,能随时看到每张表/每个项目的存储费用?
如果这些问题有超过3个回答“没有”,那你的存储治理基本还处于“听天由命”的状态。别急,这也是大多数人走过的路,后面只要按上面提到的方法逐步建立规范,会明显改观。
5. 常见问题与排查技巧实录
5.1 小文件过多
这个绝对是我见过频率最高的存储问题。典型症状是:跑批越来越慢,HDFS NameNode内存告急,任务日志里频繁报“java.io.IOException: Too many open files”。
排查步骤很简单,先看每个分区下面的文件数量。一条SQL就能查:
SHOW PARTITIONS table_name;如果某个分区文件数量上万,基本就是小文件问题。之后可以查一下Spark/Hive的计算引擎配置。如果是Spark,常见原因包括:
spark.sql.shuffle.partitions设置过大,导致落盘文件过多;- 动态分区插入时,每个分区写入的数据量差异太大;
- 流式作业频繁写入小批次,但没有做合并。
解决办法分场景。离线批处理,可以在写入后跑一次INSERT OVERWRITE ... SELECT ...重写表,并用DISTRIBUTE BY打散数据。流式写入,可以用支持的Auto Optimize功能(Iceberg/Hudi有),或者调度一个定时合并任务。不需要把小文件全部压到一个文件里,目标只是让文件大小在合理区间,比如256MB到1GB之间。
我踩过一个更隐蔽的坑:使用Flume或Filebeat这类采集工具,直接写入HDFS路径,而没有经过Hive/Spark,导致产生大量无Schema的小文件。解决办法是这类日志先落到临时目录,再由定时的Spark作业清洗并写往分区表。所以,日志链路和数仓表链路尽量分开,别让采集端直接污染数仓的存储目录。
5.2 存储成本突增
成本突增有两种常见情况:一是数据量本身涨了,二是“伪成本”涨了。第一种算正常,第二种才是治理重点。
伪成本来自几个地方:
- 副本数过高:有些团队担心丢数据,所有目录都设置了双副本或三副本,其实冷数据完全可以降到1副本加纠删码,成本立刻降下来。
- 存储类型选贵了:文件全放在SSD或者热存储,即使半年没被访问过。把生命周期规则挂上去,自动转冷,效果立竿见影。
- 不合理的临时表:开发过程中产生的临时表,分散在集群的各个租户目录下,没有清理机制,半年下来占了几十个TB。我见过一个团队,临时表占用的存储量比正式表还多,这属于存储治理失败。应该约定临时表命名前缀(比如
tmp_),并定义一个TTL,超过3天自动删除。 - 版本和快照过多:Iceberg/Hudi这类数据湖如果保留过多历史快照,存储成本会随时间线性增长。我建议快照保留天数设置成7天以内,过期快照用expire_snapshots清理。
成本治理不只是省钱,更是为了让钱花在刀刃上。每个月做一次存储成本Top 50表盘点,让业务负责人确认是否还需要保留这些数据,比任何自动化规则都有效。
5.3 数据倾斜导致查询慢
存储层的数据倾斜,和计算层的数据倾斜表现很像:跑一个GROUP BY,某个Reduce一直在执行,其他Reduce都闲着。但存储侧的倾斜往往是不合理分区或分桶导致的。
比如分区表按city分区,但分区的数据分布极不均衡——北京一个城市有1亿条,小城市只有几千条。查询时如果做了分区裁剪,还好;如果不做裁剪,一个任务被大分区拖死。分桶同样如此,如果分桶键的某个值占比过高,会导致桶内数据量差异巨大。
排查倾斜的思路是:先看每个分区或桶的行数。比如按天分区,dt='2025-01-01'的数据量是不是远高于其他日期?如果是业务规律造成的,那就要考虑在SQL中加一个“热点键”处理:将热点数据加随机后缀打散,然后union起来。
存储治理层面,倾斜问题的根治办法不多:一是选择分布更均匀的分桶字段;二是对已知热点分区单独设置存储策略;三是实在不行,在任务调度层面给大分区配置更多资源。别指望一个万能配置解决所有倾斜问题,很多时候需要根据业务数据的分布特征来定制。
5.4 元数据与权限的安全隐患
最后聊一个容易忽视的安全问题。存储层不止数据本体,元数据同样有价值。比如表名包含了客户信息、字段名暴露了业务逻辑,这些元数据如果没有权限管控,一个低权限账号就能看到所有表结构,等于把业务底牌亮了出去。
我在一个项目里就碰到过:一个新来的数据分析师,只需要访问4张表,结果因为数仓的LDAP组授权太粗,整个部门的表权限都给了他,他连财务明细都可以看。这就是存储层缺了字段级权限。所以现在我做治理时,不管平台支不支持,都会先定义一个“权限最小化”原则:
- 库/表级权限严格控制,默认拒绝;
- 敏感表必须有单独的权限组,并加审批流程;
- 每次授权都要设定有效期限;
- 重点是审计日志,定期拉一遍,看有没有越权访问。
数据存储的安全问题,往往不是一次大事故,而是无数个小疏忽的累积。等到数据泄露才想起权限治理,已经晚了。
最后分享一点我自己的体会
这套“数据治理概论”看到第5章,我最大的感受不是存储技术本身有多难,而是存储治理的好坏,反映了一个团队对数据资产的态度。有的团队数据量不大但井井有条,因为他们舍得在元数据、格式、生命周期上花功夫;有的团队数据量巨大但一团乱麻,因为所有人都在赶业务进度,没人愿意停下来立规范。我做过的项目里,凡是存储治理做得好的,后面的建模、指标、质量治理都会顺很多,凡是存储一塌糊涂的,上再多治理平台也是白搭。
最后再分享一个小技巧:如果你们团队刚起步,实在没法一步到位,那你就抓三件事——第一,统一文件格式和压缩规范;第二,强制表/字段注释与目录命名规范;第三,建立冷热分层和过期清理的自动化定时任务。这三件事做完,存储治理的框架就算立起来了,后面再逐步补权限、补血缘、补质量,都不会是无根之木。
第5章内容很多,我也只能挑自己实战中交集最多的部分展开。现在我可以非常确定地说,数据存储绝不是“随便存一下”的事,它值得每个数据从业者花时间去理解、去设计、去维护。希望你读完这篇,能对存储治理有新的理解,也能在实际项目中避开我踩过的那些坑。