3. 基于S3的数据湖分层设计
在批流一体平台中,S3 不只是“存文件的地方”,而是批处理与流处理共享的唯一事实存储层。批任务按小时或天读取历史数据,流任务按秒级持续写入增量数据,两者必须落在同一套目录、同一套分区语义和同一套元数据约定上,否则就会出现“批看不到流刚写的文件”“流覆盖了批正在读的分区”这类问题。因此本节的核心任务是:用一套可被 Spark、Flink、Athena、Glue 共同识别的目录与分区规范,把 S3 组织成原始区、清洗区、聚合区、消费区四层,并为每层确定文件格式与生命周期策略。
四层分区的职责与数据流
四层不是简单的“复制四份”,而是数据在保真度、结构度、查询效率三个维度上的递进。原始区(Raw)保留接入时的原始字节,不做任何字段裁剪,用于回溯和重放;清洗区(Cleaned)完成解析、去重、类型规范、脏数据隔离,是批流真正对齐的“标准层”;聚合区(Aggregated)按业务粒度预计算指标,供 BI 与报表低延迟查询;消费区(Serving)面向具体应用做列裁剪、宽表化或导出,通常体量最小、访问最频繁。批流共用的关键在清洗区:流任务写入的微批文件与批任务写入的历史文件必须使用相同的分区键和相同的 Schema 版本。
目录规划:用前缀表达层级与归属
目录规划的第一原则是前缀即语义。推荐使用 s3://<bucket>/<layer>/<domain>/<table>/<partition>/ 的结构,例如 s3://dl/cleaned/orders/dt=2024-06-01/hour=00/。其中 layer 固定为 raw、cleaned、agg、serving 四选一,domain 表示业务域(orders、users、events),table 是逻辑表名。这样做的直接好处是:Glue Crawler 可以按前缀分别建库,Athena 查询时能通过分区裁剪跳过无关目录,IAM 策略也能按前缀做最小权限控制——例如只允许 Flink 任务写入 cleaned/ 前缀,不允许它删除 raw/ 下的历史文件。
需要特别注意的是,不要在同一前缀下混用不同 Schema 版本。如果 orders 表在 6 月新增了 coupon_id 字段,应通过 Schema Registry 或 Glue Catalog 的版本管理记录变更,而不是直接覆盖旧文件。批任务读取历史分区时按旧 Schema 解析,流任务写入新分区时按新 Schema 写入,两者通过分区边界自然隔离。
分区策略:批流共用的时间粒度
分区键的选择直接决定批流能否对齐。推荐以事件时间而非处理时间作为分区依据,因为流任务可能因网络延迟晚到,若按处理时间分区,同一事件时间的数据会散落到不同分区,批任务无法按时间范围完整读取。常用分区键为 dt(日期)加 hour(小时),对于高吞吐场景可细化为 dt/hour/minute,但分区过细会导致小文件问题。
一个可参考的示例参数:假设 orders 主题日均 5000 万条记录,单条清洗后约 400 字节,则日增数据约 20 GB。按小时分区后,每个分区约 0.83 GB,配合 128 MB 的目标文件大小,每个分区约 7 个 Parquet 文件,属于健康范围。若按分钟分区,每个分区仅约 14 MB,会产生大量小文件,反而拖慢 Athena 查询。因此小时分区是批流一体场景的常用平衡点。
文件格式选择:按层差异化
原始区建议保留接入时的原始格式(JSON、CSV 或 Avro),因为这一层的目标是保真与可重放,压缩可用 gzip 或 zstd 降低存储成本。清洗区与聚合区统一使用 Parquet + Snappy:Parquet 是列式存储,支持谓词下推和列裁剪,Athena 查询时只读取需要的列;Snappy 压缩比适中、解压速度快,适合批流共用的高频读写。消费区若面向特定应用,可进一步转为 ORC 或直接导出为 CSV/JSON 供下游系统消费。
FileSink 并配置滚动策略:rolling-policy.file-size = 128MB、rolling-policy.rollover-interval = 10min,避免单个文件过小或过大。批任务则通过 spark.sql.files.maxPartitionBytes 控制读取时的分区大小。生命周期管理:分层设置过期与转储
生命周期策略应按层差异化,而不是一刀切。原始区数据保真但查询频率低,可设置 90 天后转 Glacier 归档;清洗区是批流共用的核心层,保留 1 年后转 IA(低频访问);聚合区数据量小、查询频繁,保留 2 年;消费区按应用需求设置 TTL,例如报表宽表保留 30 天即可。在 S3 控制台或通过 put-bucket-lifecycle-configuration 配置时,注意规则要按前缀精确匹配,避免误删正在被流任务写入的分区。
Expiration 会直接删除对象,且不可恢复。建议先对 raw 和 cleaned 层启用版本控制,再配置过期规则;对于 agg 和 serving 层,可先用 NoncurrentVersionExpiration 清理旧版本,确认无误后再启用当前版本过期。批流共用存储的落地检查
完成上述设计后,可用以下清单快速验证是否具备批流共用条件:分区键是否统一为 dt/hour 且基于事件时间;清洗区 Schema 是否通过 Glue Catalog 统一注册;流任务与批任务是否写入同一前缀且文件格式一致;生命周期规则是否按层配置且未误伤活跃分区;IAM 策略是否按前缀做了最小权限隔离。这五项全部满足时,S3 才真正成为批流一体的共享存储底座,而不是两套系统各自为政的“文件堆”。