3. 数据湖目录结构与分区设计

数据湖最容易失控的地方,不是存储容量,而是“路径语义”。当不同团队各自按习惯把文件丢进容器,几个月后就会出现 /data/2023/final_v2/、/raw/customer_new/ 这类无法治理的路径。企业级目录规范的目标,是让路径本身携带可解析的业务域、数据层级、来源系统、时间与分区信息,使数据工程师、分析师和治理工具都能仅凭路径判断数据含义与生命周期阶段。

一、目录规范:用路径表达数据契约

推荐采用“容器/文件系统 → 数据层级 → 业务域 → 数据集 → 分区列”的层级结构。以 ADLS Gen2 为例,容器名对应环境与用途,目录名对应层级与业务语义:

abfss://lake-prod@companydatalake.dfs.core.windows.net/
  bronze/            # 原始层,保留源系统原貌
    sales/
      orders/
        source_system=erp/
        ingest_date=2025-03-01/
          part-00000.parquet
  silver/            # 清洗整合层
    sales/
      orders_cleaned/
        order_date=2025/03/01/
  gold/              # 服务层,面向主题域
    sales/
      daily_sales_summary/
        report_date=2025-03-01/

路径中的 bronze/silver/gold 是数据成熟度层级,sales 是业务域,orders 是数据集,source_system、ingest_date、order_date 是分区列。这种结构让路径成为可被 Spark、Synapse、Purview 自动解析的元数据来源。命名统一使用小写字母、数字和下划线,避免空格、中文和大小写混用,因为 Hive 风格分区在部分引擎中大小写敏感,混用会导致分区裁剪失效。

目录规范一旦确定,应写入数据平台接入标准,并在 CI 中校验新数据集路径是否符合正则表达式,例如 ^abfss://lake-(dev|test|prod)@.+/ (bronze|silver|gold)/[a-z_]+/[a-z_]+/。

二、分区策略:在裁剪效率与文件数量之间取平衡

分区的本质是把“扫描全表”变成“只扫描相关目录”。分区列选择应遵循三个原则:高基数、查询过滤频繁、数据分布相对均匀。日期通常是最优分区列;业务域、来源系统适合作为上层目录而非分区列;客户 ID、订单 ID 这类高基数字段不适合直接做分区,否则会产生海量小目录。

以订单事实表为例,若按 order_date 按天分区,每天约 200 万行、单文件 128 MB,则每天约 15~20 个文件,一年约 7000 个文件,属于健康范围。若再叠加 region 分区,每天 5 个区域,文件数变为 75~100 个/天,一年约 3 万个文件,仍可接受,但需评估元数据压力。若继续叠加 channel,文件数会迅速膨胀到数十万,此时应改用 Z-Order 或 Liquid Clustering 等文件内聚技术,而不是继续增加分区列。

企业级数据湖目录与分区设计示意 目录层级(路径语义) 容器:lake-prod 层级:bronze / silver / gold 业务域:sales / finance / hr 数据集:orders / customers 分区列:order_date=2025-03-01 文件:part-00000.parquet 路径即契约:可解析、可校验、可治理 分区裁剪效果对比 未分区全表扫描 扫描 365 天 × 20 文件 = 7300 文件 读取约 900 GB 按 order_date 分区 仅扫描 1 天 × 20 文件 = 20 文件 读取约 2.5 GB,裁剪率 99.7% 叠加 region 分区 1 天 × 5 区域 × 4 文件 = 20 文件 文件更小,元数据压力上升 分区列越多,裁剪越细,但小文件风险越高 格式与生命周期 Bronze:Parquet + 原始 JSON 保留源格式,便于重放 Silver:Delta Parquet ACID、时间旅行、Schema 演进 Gold:Delta / 聚合表 面向 BI 与报表,列裁剪优先 生命周期:热 → 温 → 冷 30 天热、90 天温、365 天归档 格式决定能力,生命周期决定成本

三、文件格式选择:按层级匹配能力

Bronze 层应尽量保留源系统原始格式,例如 ERP 导出的 JSON、CSV 或 Avro,以便在解析逻辑出错时重新处理。Silver 层统一转换为 Delta Parquet,因为 Delta Lake 提供 ACID 事务、时间旅行和 Schema 演进,能支撑清洗、去重和合并操作。Gold 层继续使用 Delta,但应控制文件大小在 128 MB~1 GB 之间,避免过多小文件拖慢 BI 查询。

Parquet 的列式存储使查询只读取必要列,配合分区裁剪可显著降低 I/O。对于需要频繁更新的维度表,Delta 的 MERGE 比覆盖写更高效;对于只追加的事实表,直接 INSERT 即可。若使用 Synapse 无服务器 SQL 池直接查询文件,需注意 Parquet 的嵌套类型支持较好,而 CSV 需要显式定义 Schema。

四、生命周期管理:让数据自动降温

生命周期策略应基于“访问频率 + 合规要求”制定。以 ADLS Gen2 为例,可在容器或目录级别配置生命周期管理规则:

{
  "rules": [
    {
      "name": "bronze-to-cool",
      "enabled": true,
      "type": "Lifecycle",
      "definition": {
        "filters": {
          "blobTypes": ["blockBlob"],
          "prefixMatch": ["bronze/sales/orders/"]
        },
        "actions": {
          "baseBlob": {
            "tierToCool": { "daysAfterModificationGreaterThan": 30 },
            "tierToArchive": { "daysAfterModificationGreaterThan": 365 },
            "delete": { "daysAfterModificationGreaterThan": 2555 }
          }
        }
      }
    }
  ]
}

该规则表示:Bronze 层订单数据 30 天未修改转入冷层,365 天转入归档层,7 年后删除。Silver 和 Gold 层因访问频率较高,可设置更长的热层保留期,例如 90 天转冷、730 天归档。归档层的数据读取延迟较高,适合合规留存而非日常分析。

生命周期规则按“最后修改时间”判断,而 Delta 表的 _delta_log 会持续更新,可能导致整个表被误判为热数据。建议对 Delta 表按分区目录分别设置规则,或使用 Delta 的 VACUUM 与保留策略配合,避免归档层数据被意外删除。

五、落地检查清单

检查项合格标准常见问题
路径命名小写、下划线、无空格大小写混用导致分区裁剪失效
分区列日期 + 低基数业务列,不超过 3 个用客户 ID 分区产生海量小目录
文件大小128 MB~1 GB流式写入产生大量 KB 级文件
格式Silver/Gold 使用 Delta ParquetGold 层仍用 CSV 导致 BI 查询慢
生命周期按层级和访问频率分层配置全湖统一 30 天删除,误删合规数据

目录结构与分区设计不是一次性任务,而是随数据增长持续调优的过程。建议每季度检查一次分区文件数量、平均文件大小和冷热数据分布,当发现某分区日均新增文件超过 200 个或平均文件小于 32 MB 时,就应触发合并或重新评估分区策略。把路径、分区、格式和生命周期四者绑定为统一规范,数据湖才能从“文件堆”变成可治理、可演进的资产平台。