构建 Medallion 架构——深入解读Medallion架构

183 阅读57分钟

第 1 章中,我们回顾了 SparkDelta Lake 的演进,并引介了 Medallion 架构。这一设计模式帮助在现代湖仓(lakehouse)中以逻辑方式组织数据:通过 Bronze / Silver / Gold 三层,让数据在摄取—转换—装载至各目的地的生命周期中逐步精炼

向组织解释 Medallion 架构,常有“一打开就是一窝蜂问题”的感觉:每层本应各管其职,但缺乏清晰定义与描述性指南,反而问题多于答案,造成反复困惑与低效。

尽管“三层设计”很受欢迎,关于各层的范围、目的与最佳实践仍争论不断;更有甚者,理论与实践之间的落差不小。本章我将以理论视角分享自己在设计三层时的实战见解。而在第二部分(Part II) ,我们会从理论转向实践,把本章的洞见带入实操演练,手把手搭建一套真实的解决方案架构。

三层设计(The Three-Layered Design)

在深入讨论每一层的细节前,先把握三大主层(Bronze / Silver / Gold)的高层目标与职能。图 3-1 展示了数据从 BronzeSilver 流向 Gold 的路径,并标注了摄取、处理与分析使用等关键过程。

image.png

图 3-1 Medallion 架构的三层:Bronze / Silver / Gold

在图 3-1 中,各层在数据转化与精炼中分工明确,形成从原始采集可用于价值创造的结构化演进。虽然我在第 1 章已做过概述,本节将给出每层的关键特征,为稍后对技术与流程的深入铺路:

Bronze 层

作为原始数据的汇集区,保存来自多源、不经转换的数据原貌,用作历史留存单一事实来源,确保数据被可靠采集与存放,可供后续处理。
要点特征体量大类型多真实性强(veracity) ;为保持原貌,数据不可变(immutable)

Silver 层

对原始数据进行精炼/清洗/标准化,为更复杂的运营与分析任务做准备。此处会进行质量校验、标准化、去重与其他增强处理,以提升可靠性与可用性;该层是数据仍细粒度但已保质保量的过渡区。
要点特征:结构更规整更易查询,便于分析师与数据科学家使用。

Gold 层

面向具体的业务洞察与决策,对精炼后的数据进行聚合、汇总与增强,支撑高层报表与分析;强调性能、易用性与可扩展性,为关键指标与洞察提供快速访问
要点特征:数据高度策展/优化面向消费,支撑战略级业务与决策。

NOTE
尽管技术从本地 RDBMS 走向解耦分布式已大变样,但数据仓库与数据建模的方法论仍然适用。若你对这些基础尚不自信,建议温习 Kimball Group 的数仓/维度建模著作,以及 Christopher Adamson 的 Star Schema: The Complete Reference(McGraw Hill Osborne Media)等资源,它们能为理解数据建模原理打下坚实基础。

在进入各层细节前,请先调整观念:把这三层视为“逻辑层”而非“物理层” 。例如谈 Bronze 时,不应把它框定为唯一的一层物理落地;应将其视为可能跨越多个物理层的逻辑概念。这样会让你的设计更灵活、结构更顺畅。说到这里,我们开始吧。

Bronze 层(Bronze Layer)

Medallion 架构始于 Bronze 层。该层的主要目标是以原貌存放来自各类来源的数据,不做任何修改。它确保数据易于被后续处理获取,并使用户能够立即探索与分析——本质上,它是一个可查询的原始数据蓄水池

接下来的小节中,我们将先讨论处理分层(processing hierarchy)及其如何影响潜在的 Bronze 层整体设计;随后围绕归档与管控目标,讨论全量与增量装载历史化(historization)模式管理(schema management)技术性校验;最后在转向 Silver 层之前,给出 Bronze 层的使用方式要点小结

处理分层(Processing Hierarchy)

Bronze 层究竟是单一物理层还是由多个子层组成,取决于数据源复杂度组织需求。在第 2 章中我们已提到可能存在额外着陆区,以及这些区域是否算作 Bronze 的一部分。这里用一个示例重访该话题并作出结论。

传统源系统经常限定导出格式,典型如 CSVJSON。设想一个遗留应用只能把数据导出到本地文件存储。在这种情形下,你会通过批处理把数据集成进 Medallion 架构。

一个典型的流程是:先把数据暂存到着陆区(landing zone) ,在那儿进行最小化处理,如解压压缩包校验 checksum生成元数据;随后将数据复制到 Bronze 层,并转换为如 Delta Lake 的格式以提升性能;同时做基础校验以确保数据完整性;可选地对数据进行分类加密以保护敏感信息;最后将本次交付与既有 Bronze 数据集进行对比与合并,以保持一致与完备。第 5 章我们会用外部表Delta 副本实现这些细节。

NOTE
为防止**个人可识别信息(PII)**被未授权访问,原始数据加密至关重要。因此在数据落入 Bronze 之前就进行加密并不少见。可以使用开源加密框架(如 Fernet)或其他工具完成加密。

处理分层上,Bronze 层常包含多个子层:内部暂存区处理与整理区,最后在 Delta 格式中持久化存储。因此,Bronze 是单层还是多子层,取决于数据源的复杂度、组织的要求,以及是否把着陆区算作 Bronze 的一部分。

确立了处理分层后,还需关注数据随时间的处理与管理,因为每个数据源都不相同、交付方式也差异很大:有的提供全量导出,有的仅提供增量,还有的持续流式传输。因此,选择合适的处理模式很重要。下面依次探讨:先讲全量装载,再讲增量装载

全量装载(Processing Full Data Loads)

全量装载是把整个数据集从源系统转移过来(而非仅变化部分),以确保下游处理可用全量数据。处理全量装载时,往往按固定间隔处理大批量数据,可选择先落入着陆区。通过校验后,将数据以原始形态存放,并与以往所有交付一起积累在文件夹中。在这一阶段通常只做极少量转换;有时会过滤数据或加密敏感列。当数据积累到一定程度,通常会把最近一次交付转换为 Delta Lake 格式,或作为外部表暴露,供下一层处理。第 5 章将更深入展示全量装载的模式与示例。

增量装载(Processing Incremental Data Loads)

增量装载(delta loads)¹ 常用于只需处理数据变化更新的场景,相比全量装载更加高效并可缩短处理时间。在该模式下,你通常只接收到已变更数据(例如事件)。这些数据通常先落到预备层,在此可先做轻量转换:如更换文件格式(JSON→Parquet)、转换为 Delta Lake、或做一些简单的数据类型修正。随后,你可以使用追加(append)合并(merge)的方式,将新到的数据与 Bronze 中既有数据整合。下面展开两种方式:

追加(Append mode)

Delta Lake 中,“append”表示新增数据被写入既有 Delta 表,不修改/删除已存在数据。适用于需要持续追加记录的场景(如日志/流式数据),不会影响既有数据。

例如,df 表示一个 DataFrame。可以先读取数据,再追加到 Delta 表:

df = spark.read.json(f"{filePath}/events/*.json")
df.write.format("delta").mode("append").saveAsTable("events")

Append 不处理更新或删除,只是把新数据接在末尾。Merge 更复杂,它既能插入新记录,也能基于键更新既有记录。

合并(Merge mode)

Delta Lake 中,“merge”支持对 Delta 表执行 upsert(更新+插入)。当需要在一次操作里根据条件同时更新插入记录时尤其有用。

设想你维护一张客户表,包含过去 180 天仍在源系统中的客户状态标记 is_current。通过对 Delta 表执行 MERGE,可以高效同时完成多种变更:

  • 插入新客户;
  • 更新最近回流客户的状态;
  • 修改已不活跃客户的状态。

Delta Lake 在后台高效处理这些操作,确保数据完整性流程顺畅。第 5 章会再回到这种 merge 思路。

增量装载通过只处理自上次装载以来的变化,来高效管理大数据集。对于拥有百万级行数的大表,这尤为有价值。同时,增量也有助于在 Silver/Gold 等层上进行高效刷新,避免每次都重处理全量。增量的一个实践要点是与Change Data Feed(CDF)结合,把增量变化推送到下游层。

TIP
结合 Delta Lake + Spark 的增量处理,并使用如 trigger(AvailableNow=True) 等配置,可更高效、经济地管理数据。

要让增量装载有效运行,源系统表需具备唯一的增量标识updated_at 列,以便识别新增/更新记录;同时要确保源系统不会更新早于上次抓取点的旧记录。若可能出现回写旧记录,常规增量可能会漏变更,此时应使用 CDC 等工具——它们从事务日志捕获插入/更新/删除,更可靠地保障完整同步

对于流式处理,通常会基于 Delta(或 Iceberg)事务日志指定起始版本(startingVersion) ,这样可精准续跑,无需额外命令。

或者,你可以在 Bronze 的标识/updated_at 列上取 max,得到最近处理的游标,再去源端查询大于该值的记录,仅处理新变更

建议维护一张元数据控制表来追踪已处理的记录,有助于在复杂系统中管理数据流确保一致性

若想进一步理解增量 delta 装载及其实践,可参考视频 Incremental Refresh with Your Warehouse Without a Date in Microsoft Fabric,其中包含全面的洞见与示例。

通过以增量形态捕获新到数据,你可以将其与完整历史数据集整合。这一历史化过程不仅用于构建全面归档,也确保现势数据能被下游及时使用。下面展开这一话题。

Bronze 层中的数据历史化(Data Historization Within the Bronze Layer)

Bronze 层的首要用途是存放完整快照/副本,类似传统数仓中的暂存区(staging) 。如果你的方式是全量抽取并覆盖,Bronze 能帮助你维护所有数据交付完整历史;它也常作为长期归档(多年保留),对审计非常关键。当然,某些已处理且不再需要的数据也可能需要清理

在 Medallion 架构中,若采用高效格式(如 Parquet/Delta)并按文件夹组织,归档相对简单。² 通过把数据组织为按时间分区的表,并使用 YYYY/MM/DDdatetime 命名的文件夹结构,可使数据有序易管。这种体系化组织不仅整洁,也便于访问与审计;当需要回看某天的数据时,可轻松重建其状态,确保准确取回历史。

需要注意:Bronze 层旨在完成类似 SCD2完全处理后的“历史维表” 。Bronze 的数据通常视为不可变(只读) 。虽然你可以追加合并来让数据集随时间增长,但在此阶段不以更新/处理为常态

NOTE
可以通过 Delta Lake 的“时光回溯(time travel)”实现数据版本化。此功能主要用于数据恢复、审计复现实验/报表,而非面向长期、全面的历史归档。

历史化在处理 Bronze 级数据时非常关键:它支持随时间归档数据迭代,这对审计/调试很重要;也支持回装(reload)与恢复(recovery) ,以应对低质量数据破坏性模式变更,让你能回到旧版本并重新处理

而在深入历史数据处理时,你会遇到一个重要挑战:模式演进(schema evolution) 。随着业务发展与数据源频繁更新,数据库模式可能发生变化,从而导致处理不一致。因此,有效管理模式变化至关重要。下一节我们将展开讨论。

架构演进与模式管理(Schema Evolution and Management)

当数据进入系统时,Bronze 层在应对高度多样且不断变化的模式(schema)方面至关重要。此阶段有效处理模式演进,为后续的全部数据处理与分析奠定基础。你既可以在 Bronze 层处理传入的模式变化,也可以等数据进入 Silver 层后再处理——何时处理主要取决于你的具体需求数据特性

在管理模式时,组织面临多种设计抉择,每种都对应不同的演进方式:

读时定义模式(Schema-on-read)

读取数据时动态应用模式。数据存储时不强制特定 schema,只有在处理(读取)时才推断或施加 schema。这种灵活方式要求系统在摄取后即刻识别并必要时适配多种 schema,尤其适用于半结构化/非结构化数据,或虽为结构化但无内建约束的数据(如 CSVParquet 文件)。

写时定义模式(Schema-on-write)

写入数据到存储时即定义表/列名、数据类型、主键等 schema,要求数据一开始就符合该 schema。相较 schema-on-read 更严格,常用于结构化数据

Delta Lake 中,若没有预定义 schema,系统会用待转换为 Delta 的 DataFrameStructType建立初始 schema(在 Apache Spark 中,StructType 是定义 DataFrame 列结构与类型的容器)。

Bronze 层schema-on-read 很常见:数据以原始格式存储,摄取时不强制 schema。这样无需预先定义结构,能从容应对数据结构变化。比如,你按 YYYY/MM/DD 进行按日分区,将每天的数据以 Parquet 文件放入对应日期文件夹;借助 schema-on-read,你可即刻跨多天读取并分析这些数据,而不必预先定义结构。不过,需要留意若干要点:

  • 仅靠 schema-on-read 无法“盲目处理” :你可能仍需检测并记录 schema 变化处理失败、利用 Delta Lake time travel恢复,并为后续阶段添加与 schema 相关的元数据。这些内容将在“Databricks Auto Loader”详述。
  • 还需定义数据更新方式(尤其关系到历史化与集成):可持续追加或与既有数据合并,也可整体覆盖,或采用基于时间的分区。选择取决于数据性质、用途、系统要求和性能考量——例如事务类数据通常递增,适合追加;若源端会定期纠错,或只关心最新快照,则可选择覆盖;时间敏感/量大时,按时间分区能提升查询效率。³

因此,Bronze 层往往结合 schema-on-read 与 schema-on-write:在着陆/预-Bronze 区先用 schema-on-read(读取时不显式声明 schema),随后当数据进入可查询的层次时,切换到 schema-on-write。此关键层宜选用支持模式演进的存储(如 Delta Lake),它允许 schema 随数据演进而更新,无需整体返工或重装,从而在变化中保持数据可用。我们稍后会再回到这一能力。

但也要警惕:若源系统频繁变更,保持 schema 一致会更有挑战,需要更严密的模式管理策略以确保完整性与可靠性。因此,通常需要在摄取过程中就确立与源系统一致的 schema,遵循既定标准,可通过 DDL,或用 Spark SQL / Databricks Auto Loader 编程式设定。第二部分会更详细展开。

mergeSchema 与模式强制(Schema Enforcement)

Delta Lake 提供一系列模式管理能力(模式演进与模式强制)。具体采用哪些特性取决于系统需求与设计,并可横跨 Medallion 的各层。

例如,当数据演进需要修改表结构(如新增列)时,mergeSchema 可简化该过程:在 Apache Spark 写入操作中增加 .option("mergeSchema","true"),Delta Lake 会按以下规则自动调整:

  • DataFrame 有而 Delta 表没有的列 → 新增列,既有行该列值为 null
  • Delta 表有而源 DataFrame 没有的列 → 保持不变,新记录该列为 null
  • 新增 NullType 列 → 既有行该列值为 null
  • 同名但不同类型的列 → Delta Lake 尝试类型转换,若失败则报错。⁴

发生错误时,可利用 表历史回退到早期版本(time travel/RESTORE),再按正确 schema 重处理。更多细节可参见官方博客 Delta Lake Schema EnforcementDelta Lake Schema Evolution

若需要更严格地处理变化,可使用 Delta constraints,这与 schema-on-write 高度一致:严格校验写入数据是否匹配表 schema;不符合即拒绝写入,以维持一致性与完整性。但这会让应对持续且破坏性的源端变化更困难。

当出现 mergeSchema 无法兼容的变化时,你需协调 schema。常见做法包括:

  • SQL ALTER COLUMN
    直接在 SQL 层修改,需要源系统所有者数据平台管理员密切配合。可将操作/脚本纳入代码库以实现可追溯与审阅。方式精确但可能费力且易错
  • 自动化模式演进
    构建工具/脚本以检测源端变化,并自动生成/执行目标端所需的 ALTER 语句。详见“Handling Schema Evolution”。
  • 元数据驱动框架(推荐)
    维护旧/新目标 schema,建立映射关系,以系统化方式管理变化与复杂转换,减少人工介入。确保元数据仓始终最新且准确;可与 CI/CD 集成自动更新与部署。第 5 章会再呼应此法。
  • 限制破坏性变更
    要求源系统团队仅做向后兼容的改变,减少更新频率与复杂度。
  • 启用新版本管道
    对极具破坏性的变化,创建新版本的数据管道新目标位置,并行维护多版本 schema;应用/流程可显式选择要对接的版本。

随着我们探讨如何让数据结构适配业务需求的变化,可以明显看出:要可靠地管理这些变化,必须实施健壮的技术性校验(technical validation checks) ,以发现错误与未知变化。两者相辅相成,显著提升数据管理质量。

技术性校验(Technical Validation Checks)

技术性校验至关重要:数据完整性、准确性、完备性与一致性问题都会对组织产生严重的运营与战略影响。在湖仓架构中,Bronze 层原始(多为非结构化)数据初始落地库,其原生格式常包含不一致或错误

在此阶段实施格式/模式/完备性等校验有多重意义:首先确保数据满足既定标准,在多源/不稳定结构的前提下保证一致性;其次,及早发现完整性问题,特别是在采用 schema-on-write 时,可阻断错误向 Silver/Gold 扩散。

Bronze 层设计应突出强校验与可观测性:可一次性做全量校验,也可分段实施,或在向 Silver 传输时内嵌校验。很多公司以脚本结合元数据仓(记录源系统的技术 schema)来自动施加校验规则(声明式或动态)。

Bronze 的设计还取决于你对数据完整性问题的容忍度

  • 若下游无法容忍问题或你预期会导致失败,宜中止管道侵入式质量处理):将合格数据问题数据分别存放于不同目录/表,便于定位修复,且问题数据不会进入真正的 Bronze。
  • 若可容忍短期问题,可继续处理(非侵入式质量处理):合格与问题数据共同进入目标 Bronze 目录/表,但需在**后续层(通常是 Silver)**补救。

大多数校验都用来验证技术性方面,以防不达标的数据流入下一分区/区域。若数据未达标,数据所有者应与应用团队/数据工程师协作修复,确保满足下游消费者/用户提出的完整性要求

由此,Bronze 层既是原始数据存储库,也是关键质量闸口,确保只有满足预设质量标准的数据才能进一步进入湖仓架构。这种前置、主动的管理方式有助于在数据全生命周期内守住完整性与可用性

可用于摄取、校验与编排的一些工具与做法包括:

  • 利用 Delta Lake模式校验能力应对兼容性与一致性问题。Schema enforcement 能确保开发者遵循既定标准、保持表干净:写入时若数据 schema 与表 schema 兼容则通过,否则回滚事务、拒绝写入。
  • 使用数据质量框架/工具:如 Delta Live TablesGreat ExpectationsdbtAtaccamaMonte Carlo Data。第 6 章会比较它们的差异。
  • 自研方案:以脚本/代码与 schema 元数据进行校验,满足现有工具难以覆盖的特定要求
  • Azure Data Factory 支持schema 漂移检测(schema drift detection) ,降低上游变化对你的脆弱性。

现在我们已经覆盖了若干设计考量,接下来看看 Bronze 层的使用方式及其在更广阔的数据利用语境中的角色。这将帮助我们理解其重要性以及与更大范围数据管理框架的整合

使用与治理(Usage and Governance)

有些数据从业者认为,业务用户也能从直接查询或进行 Bronze 数据的临时分析中受益。然而,原始数据往往充满挑战:它通常要求对源系统的设计及其复杂业务逻辑有深入理解;大量小表的存在也降低了可用性;与原始源系统的强耦合在源系统发生变更时会带来显著困难。上述复杂性常常让人不鼓励Bronze 层数据直接用于业务分析。

为确保妥善的数据治理,应实施严格的访问控制,防止未经授权的访问、操作或删除。Bronze 层数据是不可变(immutable)的,即保持其原始状态(在处理过度技术性数据时可能存在少量例外)。这种不可变性要求维护详尽日志,记录数据摄取时间、来源以及任何系统交互,以实现准确的可追溯性

同样重要的是设置告警以监控数据大小、格式或到达时间的异常,从而维护数据管道的完整性。需要制定并定期更新事件响应预案,以有效应对错误数据交付相关的问题。还建议落实最佳实践:例如详尽记录变更、维护**清晰的参考目录(catalog)**等。

本质上,Bronze 层是 Medallion 架构中的基础暂存区:它汇聚来自多源的原始数据,并通过校验格式转换(如转为 ParquetDelta)进行处理。虽然该层数据通常是不可变的,但也有例外——尤其是当需要用元数据丰富、或为了敏感信息治理而应用过滤与转换时。因而本层数据既可能是完全原始,也可能是略有增强

实践中的 Bronze 层(The Bronze Layer in Practice)

Bronze 层的使命,是以最真实的形态捕获并存储原始数据。它是可靠的输入基座,稳定地摄取并保留全部源数据的未改动副本,防范数据丢失损坏,从而为后续的**(重)装载与处理**提供可信根基。

Bronze 数据层不应被视为僵化的:它应根据组织的特定需求进行裁剪。这可能包括增加额外着陆区为增量交付设置预处理子层、以及数据验证停泊区,如图 3-2 所示。或者,你也可以将(部分)Bronze 视作虚拟层:数据并非物理存储,而是以对源系统的逻辑表征呈现。通过把设计贴合组织需求,可以最大化 Bronze 层的效能。

image.png

图 3-2 实践中的 Bronze 层形态示意

尽管 Bronze 层对于数据完整性历史准确性不可或缺,但其原始性限制了其在分析场景中的直接适用性。此状态下的数据常含有不一致、冗余与异常,若不加处理就会扭曲洞察。此外,Bronze 与源系统强耦合,也引入了风险与依赖。因此,推进到 Silver 层就显得必不可少

Silver 层(Silver Layer)

Bronze 层完成校验并将数据置于可查询状态后,我们可以进入 Silver 层。此层聚焦于清洗与增强:对日期与时间等要素进行格式标准化引用数据(reference data)得到强制与对齐,命名规范得到统一,重复数据被去除,并执行一系列功能性数据质量检查。此外,会丢弃低质量行过滤无关数据。尤为重要的是:在这个阶段,数据通常尚未与其他来源的数据合并或集成

与 Bronze 层类似,设计 Silver 层同样需要做出不少复杂取舍。下面我们深入这些细节:先看数据清洗活动,再讨论一个建模设计考量,随后介绍其使用方式,包括运营类查询机器学习应用

数据清洗活动(Cleaning Data Activities)

组织常会遇到源数据质量差的问题,因而需要彻底清洗。许多清洗任务可以在装载与处理阶段自动化完成,但也有一些更适合在源系统修复——因为源系统还可能向其他应用提供接口,你不希望质量问题在其他地方再次出现。因此推荐在源头修复质量问题。

清洗过程可由特定 ETL 规则映射表驱动。需要认识到:清洗流程复杂且常需多轮迭代。把数据加载进 Gold 层后,你可能会发现问题,需要返回 Silver 层,甚至Bronze 层,再行修复。

以下是清洗工作的常见方面(示例):

  • 降噪与移除不真实数据:剔除无关列/行,去掉不反映真实(golden)来源的数据,以提升质量并减少存储。
  • 缺失值处理:评估缺失数据并决定处理方式——删除、用默认值替代,或基于周边数据插补(imputation)
  • 去重:确保无重复记录(除非出于保留合规必须保留),避免扭曲分析与误导模型。
  • 裁剪空格:去除字符串首尾/多余空格,免得影响排序、检索与字符串处理。
  • 错误更正:修复拼写、大小写、单位等录入错误,并检测/纠正异常值
  • 一致性检查:统一缩写、术语与计量单位(如始终使用 NL 而不是与 NETHERLANDS 混用)。
  • 格式标准化:统一日期格式(如 YYYY-MM-DD;注意不同地区偏好可能不同,如 DD-MM-YYYY)。
  • 类型纠正:为列指定正确数据类型(数值、日期、浮点等),避免隐式类型转换拖慢查询。
  • 范围修正:校验列值是否落在规定范围内。
  • 唯一性修正:在需要唯一的列上保证唯一性。
  • 约束修正:确保不存在“孤儿列/记录”,每个子记录都能关联到父表中的有效父记录。
  • 敏感数据脱敏:对明文出现的 PII 做脱敏,满足隐私与合规。
  • 异常检测:识别并修复可能指示质量问题的异常(如销量的突然尖峰)。
  • 主数据管理(MDM) :处理并保障组织共享关键数据的准确、统一与一致。
  • 数据标准化:对地址、电话、地理位置、参考编码等进行标准化,跨系统保持一致。
  • 一致化处理(Conforming) :使用通用数据模型来对齐与协调不同系统中的信息。

以上并非穷尽。编写清洗规则需要对数据与其生成的业务流程有深刻理解。请记住:清洗之后,表结构通常与 Bronze 层保持一致。被判定为错误或被拒的数据一般不会直接删除,而是打标/过滤后存放到 Silver 层的隔离表(quarantine table) 。第 6 章会用代码示例进一步说明。

清洗之后,是否保持原结构重新建模?下面逐一讨论。

设计 Silver 层的数据模型(Designing the Silver Layer’s Data Model)

Silver 层的数据建模是 Medallion 架构中的关键议题,可选路径众多。它深受以下因素影响:采集来源的数量、这些来源是否共享关键要素/对象以支持跨源匹配/关联/合并,以及为获得一体化视角而对重叠要素进行一致化的需求。

我们按以下顺序展开:列一致化与重命名 → 反规范化 → SCD → 代理键 → 与其他来源的协调;最后简述 3NFData Vault 等方法,并收束到 Silver 的使用与治理

列一致化与重命名(Conforming and renaming columns)

在 Medallion 架构中,SilverBronze 层的表常是一一对应(结构对齐),但两层的数据呈现方式可能略有差异。
虽然各组织做法不同,但我参与过的许多团队把列重命名以统一命名规范视为 Silver 层的最佳实践,我也赞同这一点。通过重命名列注释,让字段语义清晰贴合业务,既便于数据导航/操作,也提升跨团队沟通、降低出错率。示例如下:

/** 以更友好的注释创建表 **/
CREATE TABLE silver.customer (
    CustomerID BIGINT COMMENT 'Customer Identifier',
    FullName STRING COMMENT 'Customer Full Name',
    Region STRING COMMENT 'Region Code',
    SignupDate DATE COMMENT 'First Sign Up Date',
    LastLogin DATE  COMMENT 'Last Login Date'
);

/** 表与字段名更易读 **/
INSERT INTO silver.customer
(CustomerID, FullName, Region, SignupDate, LastLogin)
SELECT
    custID AS CustomerID,
    CONCAT(f_name, ' ', l_name) AS FullName,
    rgcd AS Region,
    sigdt AS SignupDate,
    lldt AS LastLogin
FROM
    bronze.cust_data;

重命名通常伴随表内标准化/一致化(统一取值范围、规范枚举/代码等),以便后续在 Gold 层更顺畅地转换与集成。合理数据类型还能避免隐式转换拖慢查询;选择能容纳数据的最小类型也有助于节省存储。

NOTE
在 Silver 表中设定取值范围或标准化类别,与**主数据管理(MDM)**密切相关。MDM 旨在确保组织共享数据的一致与准确;在 Silver 表内做标准化与其目标一致。关于 MDM,后文“Master Data Management”会详述。

反规范化(Denormalization)

为优化查询性能,可将数据整合到更少的表,减少复杂 JOIN,从而加速查询,这就是反规范化。它有时发生在 Silver 层,在 Gold 层更为常见。如果你预期 Silver 层会被高频重载高强度读取,更反规范化的数据模型通常性能更佳

反规范化会围绕常见主题域组织数据,减少大范围 JOIN,与分布式/列式存储契合,简化结构、提升查询效率。示例:

INSERT INTO silver.customer_return
SELECT
  cus.CustomerID,
  cus.FullName,
  c.Region,
  c.SignupDate,
  c.LastLogin,
  st.OrderID,
  st.OrderDate,
  st.OrderAmount
FROM
  silver.customers cus
INNER JOIN
  silver.orders st
  ON cus.OrderID = st.OrderID
INNER JOIN
  silver.customer_details c
  ON cus.CustomerID = c.CustomerID

需注意:反规范化有意引入冗余以换取性能,因此表数据量会上升,可能对写入/维护带来影响,许多组织会配套维护与优化作业进行管理。

缓慢变化维(SCD,Slowly Changing Dimensions)

若要构建完整的历史视图,常需将数据重处理为 SCD2:为表增加 start_dateend_dateis_current 等列以追踪变化。过程复杂,因“何为变化”在源与目标间定义可能不同;业务键(business key)哈希对比在此至关重要,用于判定处理策略。

业务键:能唯一标识业务实体的属性/属性组(自然键),如 CRM 中的 CustomerID、或社会保障号。
哈希:对任意长度输入生成定长值的结果,可作数据“指纹”。

目标表结构也会影响处理:不在目标表中的列不应纳入对比。

社区里存在争论:SCD 应该做在 Silver 还是保留到 Gold。多数工程师认为 SCD2 更适合 Gold⁵(构建/存储成本高、利用率有限),因此 Silver 应以当前快照为主。
但这也有细微差异:若 Gold多源整合,而你又非常重视源端语境的真实历史(如很多 ML 模型依赖原始上下文的历史),则在 Silver 建立历史视角可能有价值;对于运营报表,保留原始历史也可能免去在源附近建设 ODS 的需要。尤其在对源系统(SaaS、外包、NoSQL 等)真实数据访问受限的环境下,这点更常见。是否在 Silver 实施 SCD,需结合具体项目权衡。

代理键(Surrogate keys)

围绕 SCD 的争论也延伸到 是否在 Silver 使用代理键代理键无业务含义、用于唯一标识记录的稳定主键(自增或通过哈希/多列拼接生成),其优势在于稳定、永不改变,适合随时间追踪维度属性变化。

我的观点是:代理键通常不属于 Silver。在后续阶段(把多来源数据汇总/合并以构建维表与事实表时)再创建代理键更合适——此时先用自然/业务键连接,再派生代理键。因此 Silver 应聚焦清洗与更好表征数据集,而非造代理键。
当然,若组织强烈偏好在所有 SCD 中使用代理键,也可在 SilverGold 同时实现:Silver 生成的代理键可作为 Gold 的查找键来定位对应代理键。此法可行,但需要谨慎实现以保证跨层一致性与完整性。

到这里,我们完成了 Silver 层设计的讨论。正如所见,Silver 与 Bronze 紧密对齐,但在呈现上有细微差别:例如列重命名标准化处理,以及底层存储为性能优化所做的调整。尽管如此,此时的数据仍主要是面向来源的,尚未与其他来源集成。接下来,我们将讨论为何需要丰富(enrichment)以及何时/如何与其他来源集成

与其他来源的一致化(Harmonization with Other Sources)

我经常被问到:在 Silver 层内,是否就应该跨来源集成,以便形成一体化/企业级视图?这类活动存在不少细微差别。

总体而言,我的建议是保持分离,以便更易管理清晰隔离关注点。为此,我建议不要过早将不同源系统的数据进行合并或集成。过早合并会在应用之间产生不必要的耦合。例如,一个只关心单一来源数据的用户,若在 Bronze 层后立刻做了合并,就可能被无意间牵连到其他系统的变化,从而受到影响。

因此,如果你的目标是维持隔离式设计,最好将跨来源的数据整合/合并放到 Gold 层。这一策略有助于维持清晰边界尽量减少系统间依赖。该理念同样适用于把你的湖仓(lakehouse)源系统侧保持一致。如果你在构建面向源系统的数据产品并强调数据所有权对齐,我会提醒工程师不要过早把来自不同领域的应用数据做交叉关联(cross-join)。

不过,也有组织倾向在 Silver 层进行跨来源集成。在这种做法中,Silver 层更像传统的数据集成层:在进入 Gold 层供最终消费之前,先在 Silver 内对数据进行合并与一致化。此路径下,常见会采用更传统的数据建模技术,如 第三范式(3NF)Data Vault。下面更详细地讨论这些模型。

3NF 与 Data Vault

第三范式(3NF) (见“Inmon Methodology”)是一种常用于运营/事务型数据库规范化技术,以减少冗余与依赖。一些从业者青睐在湖仓中使用 3NF(或其他规范化形式),因为它能带来更少冗余更强适应性更好一致性

3NF 原则之上,Data Vault 引入了另一种规范化建模技术:它在 3NF 的基础上加入了Hub(唯一业务键) 、**Link(数据关系)Satellite(明细描述信息)**等独特结构。

在实践中,Data Vault 的应用通常落在 Medallion 架构的 Silver 层:该层会同时存在Raw VaultBusiness VaultRaw Vault 提供结构化、规范化的“原始数据”表述,并映射到概念业务模型;它使用业务键整合多源数据,对模式漂移(schema drift)具有韧性,并跟踪历史变化Silver 层还包含 Business Vault 的要素,如一致化(harmonization)中间转换,以丰富数据并与企业级定义对齐;此外,PIT(Point-in-Time)Bridge 表可提升性能与查询体验,为 Gold 层的使用做准备。在这种设置下,Bronze 仍是原始数据暂存区Gold 仍负责面向 BI/分析的最终变换。

Data Vault 以其适应性见长,尤其适合数据结构与业务规则频繁变化的环境,契合复杂企业的数据需求。企业偏好在 Medallion 中采用 3NF/Data Vault 的常见原因包括:

  • 高整合需求:多而杂的源系统需要 3NF/Data Vault 来构建统一、集成且一致的数据模型。
  • 复杂企业环境:跨多个业务域或分布式团队运营,受益于其灵活性与模块化
  • 快速变化与模式漂移:Data Vault 对变化更有韧性、能适配演进中的 schema,适用于需求与技术频繁变化的环境。
  • 复杂时间维管理:可在同一记录内有效管理多条时间线(创建时间、功能处理时间、装载时间),便于审计、合规与历史分析。

尽管如此,在 Silver 层使用 3NF/Data Vault 也有代价。虽说其灵活性强、可节省存储,但在云端数据架构中常因可扩展性问题而不被优先采用。实践者通常更偏好宽表、嵌套、反规范化,以最大化云基础设施效率,避免在 Spark 节点间进行昂贵的 JOIN 与数据洗牌(shuffle) ,从而避免查询变慢。

此外,构建 3NF/Data Vault 复杂且耗时,需要对数据关系与依赖进行详尽分析与规划,并严格遵循规则以保持模型完整性与有效性。为降低难度,组织可以考虑定期评审并引入如 VaultSpeed自动化框架

再者,湖仓架构的灵活性也促使从业者思考:如果模式变化并不剧烈,是否必须采用重规范化的模型?MedallionBronze 层提供可查询的原始表,便于重载;Delta 的**时光回溯(time travel)**也能让 Silver 数据快速回退到先前版本。

TIP
想深入了解该主题,强烈推荐 Simon Whiteley 关于 Data Vault 的视频与分享。

综合来看,许多组织出于实用性会选择更大的反规范化宽表:它们通常具备更好的性能易用性更简单的整体设计。在强调性能与简洁的环境中,这类选择更具吸引力。关于此点,我们在讨论 Gold 层时还会回到。

结语:数据建模是一个需要策略性与细腻权衡的复杂议题,必须根据具体业务需求与语境量体裁衣,平衡技术、业务与运营考量。建模方式并非非黑即白;你可以针对不同用例选择不同策略

此外,即便你决定Silver 层做跨源集成,也不妨对数据施加一定程度的企业级标准化:如数据类型集中管理的参考数据命名规范。一些客户甚至会在 Silver 中实现基础业务规则(比如计算新字段),但应尽量从简Silver 的主要职责应是确保数据清洁并实现标准化,而非进行重度增强与复杂业务规则的落实。我们将通过运营类查询机器学习进一步讨论**数据丰富(enrichment)**的必要性与落点。

运营查询与机器学习(Operational Querying and Machine Learning)

机器学习在训练数据贴近业务语境时表现最佳。因此,许多组织会直接在 Silver 层开展运营类查询ML 负载。这常引出一个争论:是否需要为 ML 进一步丰富(enrich)数据?例如,把类别特征转换为数值特征可简化算法处理。很多组织也因此在典型的三层之外,额外引入沙箱/ML 层用于特征工程与模型训练

上述考量会影响你把丰富活动放在哪一层。如果你的目标是运营报表且确实需要数据丰富,我建议在 Silver 层启动丰富流程。尽管这可能在 Gold 阶段的合并时需要更多适配,但其带来的灵活性通常值得

另一方面,如果你更注重保持灵活性并倾向于关注点分离以便管理,那么可以考虑将数据丰富延后到 Gold 层。这种策略能更好地隔离职责简化治理,在处理复杂数据结构时尤其有效。

管理重叠需求(Managing Overlapping Requirements)

继续讨论数据丰富(enrichment) 。工程师之间一直争论业务规则应落在 Silver 还是 Gold 层,这通常围绕复用性标准化展开。总体上,我建议在 Silver 层保持最小化转换;而将面向终端使用的业务规则放在 Gold 层。这样既能针对具体用例做定制,也更便于更新与运维

不过,这种做法也可能带来问题:当同一套集成被其他团队频繁复用时,如果 Gold 层的数据一开始就为某个业务单元定制,另一支团队需要相同数据时,可能被迫重新构建逻辑。这强调了在 Silver/Gold分层结构设计丰富/转换落点上需要深思熟虑。讨论 Gold 层的**策展层(curated)与语义层(semantic)**时,我们会再回到结构化这一点。

自动化任务(Automation Tasks)

可扩展性的关键在于让大多数任务自动化。但要认识到:在 Medallion 架构的不同层中,并非所有处理步骤都能以同等程度标准化或自动化

就自动化而言,不同工具与框架各有考量。正如图 3-3 所示,从源系统Bronze 层尤其具有挑战,原因在于源端技术与厂商方案的多样性。例如,有的厂商仅提供特定 API专有服务来导数,导致输出格式多样;若某厂商只支持 CSV 导出,你就必须调整流程来兼容这种格式。

image.png

图 3-3 Medallion 架构中易于自动化与复杂处理步骤对比示意

跨过这道门槛后,当数据在 Bronze/Silver 层以标准化格式(Delta Lake)可用时,便能进一步推进标准化与自动化。此阶段,工具与元数据驱动框架至关重要,可让处理更加流畅自动,最大化伸缩性与效率

回到图 3-3,与“源→Bronze”的复杂性相比,“Bronze→Silver”通常更直接:此时的转换多数可预测且相对简单,如列重命名、过滤、质量修复、查找表(lookup)引用、默认值等。由于可参数化程度高,这阶段的工程更高效易管。因此,许多组织依赖元数据驱动的框架通用脚本/笔记本

这些框架允许你以声明式方式定义数据工程任务:如schema 信息、质量规则、自然/业务键、映射规则等都保存在元数据仓中,由此自动生成转换代码。只需更新元数据即可调整转换逻辑,大幅自动化数据工程流程。

其他可选框架包括前述的开源命令行工具 dbt:它以接近 SQL SELECT 的模板语法定义转换,十分高效。若在 Databricks 环境中,Delta Live Tables(DLT)也是值得考虑的声明式数据工程框架:不仅支持转换,还负责任务编排、集群管理、监控、数据质量与错误处理

在图 3-3 的最后阶段,需要根据各类消费者的独特需求交付精炼数据。这一步由于涉及复杂业务逻辑,很难完全用元数据参数化。不过,通过模板与服务也能引入一定程度的标准化,从而简化流程、提升效率与可管理性

理解并管理数据工程各阶段的差异,有助于构建可扩展框架。通过有效利用自动化工具与框架,组织可以优化数据处理流水线,使之既稳健可适应

实践中的 Silver 层(The Silver Layer in Practice)

在 Medallion 架构中,数据之旅始于 Bronze 层:来自多源的原始数据被按原貌摄取与存放,以保留结构与完整性。这为回溯至源提供可靠依据,对合规可追溯性至关重要。

进入 Silver 层后,你会对原始数据进行清洗、标准化并进行**(轻量)丰富**。核心目标是净化并更好地表征数据集:执行最小必需的转换与增强、开展质量检查,确保数据干净、可用、标准化,并可查询、为 Gold 层的进一步处理做好准备

TIP
Silver 层与源对齐(source-aligned)的数据进行细化与转换以保持运营一致性。这种做法可维持单一事实来源(SSOT) ,以更动态、可扩展的湖仓替代传统的 ODS(运营数据存储)。

Silver 层自身的结构——是单一物理层还是包含多道工序——在很大程度上取决于组织需求。例如,为增强可审计性,你可以将该层划分为三个阶段清洗一致化、以及(可选)构建 SCD

若你既要对齐数据所有权又要实现数据整合,可以设置分离的子层:其一用于面向源系统的已清洗数据,其二用于跨源一致化数据。无论采用何种方案,关键是让每个阶段边界清晰、流程一致

image.png

图 3-4 实践中的 Silver 层形态示意

通常,Silver镜像 Bronze:给出一模一样的表示,但加入关键增强——表被清洗、数据被标准化。这一简单形态往往能满足多数组织对一致性可用性的需求。不过,Silver 的架构可以根据数据复杂度来源数量以及性能/灵活性要求作出调整,以便既满足当前处理,又能在未来扩展与演进

当数据已被清理与组织好,Gold 层就会将其精炼到最具价值的形态。Gold 层把数据转化为可直接支撑决策的洞察:这里往往涉及更先进的数据建模聚合以及与战略目标对齐业务逻辑,因此处理会更为复杂。

Gold 层(Gold Layer)

当你抵达 Gold 层时,已进入整套数据架构中最复杂的部分。此层是数据精炼的巅峰,专为决策与报表而设计。因此,Gold 层中的数据会针对高性能查询与分析进行优化,确保能有效支撑关键业务职能。

Gold 层之所以复杂,源于来源系统的多样与复杂,以及把它们合并为单一一致视图这一艰巨任务。本层往往要实现大量复杂业务规则,并进行广泛的后处理:计算、丰富、面向应用的专项优化、纠错、转换、聚合等。

此外,Gold 层的工作高度受业务需求影响,而这些需求差异巨大:有的用户需要简单的扁平结构,另一些则需要包含维度与事实星型模型(star schema) 。同时,可能存在多个用户群体,其需求重叠甚至相互矛盾。这种多样性使 Gold 层成为数据架构中既挑战大复杂度高的部分。也因此,Gold 层常被拆分为多个子层/阶段来更好地管理复杂性。

在此背景下,我们先聚焦于数据模型设计:以星型模型来构建一个直观的 Medallion 设计基础。该方法与最佳实践有助于有效组织数据、便于访问与分析。有了星型模型的坚实基础后,再进一步探讨其细节与 Gold 层的更多复杂性。按部就班的方式,能同时兼顾 Gold 层数据精炼的基础面高级面

星型模型(Star Schema)

星型模型通常能满足大多数需求。它擅长对历史数据进行复杂分析,并能将数据转化为诸如 OLAP 多维体等形式。设计星型模型时要记住:其作用不仅是提升性能,更要贴合用户与数据交互的方式。把你的数据模型当作公共接口(类似 API 或函数):像一切面向用户的工具一样,需要直观、逻辑清晰,用户才能更高效地浏览与利用数据。

业务用户而言,清晰贴合语境至关重要。即便模型性能再佳,如果不符合用户在其领域中的思维框架,无法自然地“切片/切块/钻取”,那么它也不算成功。

因此,建模要尽量贴近终端用户的直觉心智模型:即星型模型。星型模型因其形状得名:中央是事实表(fact table) ,四周连接多个维度表(dimension tables) 。这种结构既高效,又便于依赖它做出业务决策的人群理解与使用。

要构建星型模型,首先需通过与干系人沟通明确业务需求;随后声明粒度(granularity) ——它决定事实表与维度表的明细层级,并确保它们可以正确关联。粒度也会决定所需的聚合层级。

STAR SCHEMA(Kimball 方法论)
事实表(Fact table) :星型模型的中心,存储用于分析的定量度量,要求紧凑、快速、可适应
维度表(Dimension table) :为事实提供业务语境,存储与度量相关的属性,让数据更可读可解
典型星型模型中,一张事实表环绕着多张维度表。维度表通常比事实表,但包含更多文本属性。这种设计便于存储与快速检索,支撑 BI 的切片/切块分析。

接着,识别将构成模型的维度。例如,一家航空运输公司可能需要时间、地点、客户、飞机等维度。⑦ 确定维度后,再定位将填充模型的事实,完成基础框架。

将星型模型“装载(loading)”到位包含两项关键任务:装载维度表装载事实表。这是让模型进入“可运营分析”状态的关键。下面分别说明。

装载维度表(Loading the dimension tables)

装载维度表的复杂点在于需处理SCD(缓慢变化维) 。这是一个增量化过程:比较新到数据维度表中已存数据,以识别新增/变更、管理代理键(surrogate keys) ,并正确插入或更新维度记录。

处理 SCD 时,ETL 会用**业务键(business key)**扫描维度表:

  • 匹配到,则更新旧记录插入一条新记录承载更新后的信息;
  • 未匹配到,则插入新记录并分配新的代理键
    需要注意:若来自不同来源的业务键可能重叠,就必须进行区分,例如引入来源系统标识来管理重叠。

在向维度表插入之前,必须对数据进行一致化(harmonization) :把来自不同系统的数据转换为公共格式。这要求识别共性属性与取值,通常需要充分的业务知识。一致化过程中可能要重组、清洗与纠正记录:解码代码拆分复合属性、将空值替换为必要的默认/强制值等。

一致化完成后,再将记录插入维度表。此过程往往经由多跳/多阶段:例如先入临时表,再迁入目标维度表,以确保维度数据的完整性与一致性

装载事实表(Loading the fact tables)

装载事实表表面看起来比维度表简单,但仍有挑战。核心是将描述业务交易的业务键替换为指向维度表的代理键。事实表的每一行都包含指向维度表行的外键

WARNING
必须先准备好维度表再装载事实表,因为事实表要依赖维度表中的代理键。如果维度表里没有可供查找的业务键/代理键,就无法为事实行赋予正确的外键。

装载过程中可能会遇到无法立即建立维度与事实关联的场景,即所谓**“提前到达的事实(early arriving fact)” 。此时需要创建占位记录(placeholder)来代表缺失的维度条目,以维护维度—事实之间关系的完整性**,保证星型模型正常工作

装载优化(Optimizing loads)

在装载星型模型时,可能出现诸如查找耗时过长导致用户长时间等待的瓶颈。可通过在表中加入管理性列来优化处理流程。例如新增 type1_hashtype2_hash 列,用于快速判定 SCD1 与 SCD2 的变化,从而加速 ETL。

NOTE
SCD1:用新值覆盖旧值,不保留历史。
SCD2:同时保留当前历史记录(在同一文件或表内)。

此外,增加 creation_dateupdate_date 等列有助于识别新增被修改的数据,从而只处理必要的数据,降低装载瓶颈。这种策略既能提升性能,也能提高星型模型维护/更新的整体效率

星型模型设计的细微之处(Star Schema Design Nuances)

管理 ETL 流程与构建星型模型本质上是一个深入而复杂的过程;前文只是介绍了若干基础概念。在你亲手搭建星型模型时,会遇到该建模方法固有的诸多细节与设计考量。你很可能会把流程拆解为多个子步骤,而最初看起来的单一 Gold 层,也会随着时间推移而演化并增添复杂性

Kimball 的星型模型方法论本质上是一套设计原则:为你或你的开发者提供可遵循的指导与通用约定。然而,不同组织在模型管理上的实践差异很大。下面通过若干替代性路径拓展视角。

策展层、语义层与白金层(Curated, Semantic, and Platinum Layers)

Gold 层 实施由事实维度组成的星型模型非常流行,但随着维度模型扩张、数据源不断引入,模型会愈发复杂;需求之间也会出现相似却不完全一致的重叠,从而推动对 Gold 层(有时甚至 Silver 层)的设计进行调整。

例如,一些组织会增加额外的“协同/策展层(conformed/curation)” ,并为采用星型模型的数据集市设置独立的语义层(semantic layer) 。偶尔,这些附加层被称为白金层(Platinum) ,以强调其高度专精与精炼。在这类架构中,组织常采用某种企业级数据建模方法:如构建标准参考表协同维度(conformed dimensions) ,以便在更大的湖仓框架内,跨多个数据集市复用标准化。尽管这种方式被视为复杂且耗时,它依然流行,因为能在组织范围内带来复用性与一致性

调整 Gold 层 设计的具体做法,取决于组织规模、计划整合的源系统数量语义建模要求,以及具体消费方的诉求。上述因素都会显著影响建模策略的实施与可持续性,因此务必因地制宜。接下来,我们看一种能简化数据模型的思路。

单大表设计(One-Big-Table, OBT)

星型模型虽益处多,但落地成本高。因此,一些实践者更偏好 OBT(One-Big-Table,单大表)做法:以速度与简单取胜,哪怕要牺牲模型的灵活性与可扩展性

OBT 指将用于分析或运营的所有相关数据置于一张大表中,而不是分散到多张表或采用更复杂的星型/雪花模型。常见理由包括:

  • 更易管理
    对不专精于数据仓库的人而言,OBT 更易懂易管;对小团队/小项目尤其有利。有人指出,维护一张大表可减少星型模型中多表与关系带来的管理开销,从而节省时间与成本
  • 潜在更好的性能
    对某些查询(尤其不需从多维度做大规模聚合的查询),单表可能更快。取消星型模型中必需的多表 JOIN,可缩短执行时间。
  • 灵活的模式
    与相对“刚性”的星型模型相比,单表的模式更易修改/扩展。在需求变化快的环境里尤为有用。
  • 数据科学家偏好
    许多数据科学工具更喜欢扁平表。OBT 也便于将数据转化为向量或图等建模所需格式。
  • 利于长期分析
    对天生带“随时间变化”的数据(如销售、用户行为),单表能够更直接地做时序分析趋势预测

但“所有数据都放在一张表”也会让查询编写更具挑战。举例:

设有一张 Orders 表,不再拆分为多张关系表,而是以多列承载所有信息,如 OrderIDOrderDateCustomerIDProducts。其中 Products嵌套数组,可存放一个或多个商品:

OrderID: 5687
OrderDate: 2024-11-15
CustomerID: 112233
Products: [
  {"ProductID":101, "ProductName":"Apple iPhone 15 Pro", "Quantity":2, "Price":1299.99},
  {"ProductID":205, "ProductName":"Dell XPS 15",        "Quantity":1, "Price":1899.99}
]

在单表中,Products 的嵌套结构会让按商品维度聚合/筛选变得复杂,需要额外展开处理。此外,OBT 往往导致跨行数据重复,这会抬高内存占用(如在 Spark 中)并拖慢性能

对单表做结构变更也很快变得复杂:例如新增一个影响大量行的新字段,通常会演变成重建整表,既耗时又耗资源。由此可见,虽然 OBT 在某些方面简化了表结构,却在查询、维护与扩展上引入新的复杂度,尤其是在数据规模与演进速度上来之后。

服务层(Serving Layer)

前文讨论了湖仓(lakehouse)的设计与建模以及多层结构。在技术架构层面,尤其是 Gold 层,你可能仍需要把数据复制到湖仓之外的其他数据库/服务(依托 Delta 表)。也就是说,可将策展/呈现层(curated/presentation)的数据下推到诸如 Azure Data ExplorerAzure SQL图数据库等服务中,以便终端用户更易访问,也满足不同业务线的多样化需求。

例如,某业务单元擅长 Azure SQL:其应用与报表工具都依赖 Azure SQL 数据库。与其由中心团队在湖仓管理数据,不如让该业务单元在其自主管理的环境中维护——他们把湖仓的数据同步Azure SQL,等同于创建了一个数据集市(data mart) 。这种做法能让业务单元节省准备数据的时间,将精力放在直接深挖洞察上。可参考 James Serra 的博文 “Serving Layers with a Data Lake”

报表工具也常见类似情况。以 Power BI 为例:它可以直连湖仓中的 Delta 表,但许多组织仍倾向于使用 Import 模式来获得稳定性能细粒度安全。在 Import 模式下,Power BI 会把湖仓数据复制到其内存引擎 VertiPaq 中。⑧ 这能显著提升查询性能与数据检索效率,尤其适合大数据集的报表模型

因此,湖仓架构常会混搭多种技术服务以满足多样化需求:用无服务器计算做临时查询、用 Spark 做大数据处理、以 Delta 表承载主体数据;同时,可能引入关系型数据库处理更复杂的查询、时序数据库服务 IoT/流式分析、以及 Power BI 等构建报表立方体。是否在 Gold 层之外补充“服务层” ,通常取决于可用性、与外部服务的兼容性、灵活性、性能与成本等因素。

这一“数据库/服务拼装”的配置在很多组织里相当常见,说明湖仓既通用可裁剪集成以适配特定需求。需要牢记的是:湖仓并非一刀切;它提供的是一个灵活框架,可随不同组织的多样化诉求而适应与演进

Gold 层的实务(The Gold Layer in Practice)

在 Medallion 架构中,Gold 层在为决策高性能分析优化数据方面起着关键作用。要实现这一点,必须与数据治理(data governance)紧密对齐,以确保合规性、完整性与安全性。务必为所有数据集做好文档与目录化,对数据的使用方式保持透明,并针对特定用例进行数据分域。对框架内的角色与职责作出清晰界定,同样能确保可问责性与最佳实践的遵循。我们将在第 11 章回到这些概念。

此外,Gold 层中存放的数据需要一种直观、可自解释且为读取而优化(参见“管理 Delta 表”)的结构,以服务于与该数据交互的多类用例。要在该层设计出有效的模型,关键在于让技术策略业务需求保持一致。这通常会带来额外的物理(子)层,如图 3-5 所示。

在数据驱动的竞争环境中,组织能够从多样的建模路径中获得灵活性收益。尽管掌握这些多元格式本身不易,你的消费者将因能为其场景匹配最合适的模型而更好地释放数据资产价值。

image.png

图 3-5 实践中的 Gold 层可能形态

结论(Conclusion)

回顾 Medallion 架构的分层之旅——Bronze、Silver、Gold——可以看到每一层都承担着独特目标:让数据从 Bronze原始形态,逐步精炼到 Gold可用于决策的形态。表 3-1 给出了各层与其关键特性的高层概览。

表 3-1 Medallion 各层概览

层级目的数据模型应用的转换文件与表格式ETL 技术
Landing源系统原始数据的落地区源端原样(as-is)无(as-is)交付文件格式,如 CSV、Parquet、JSONAzure Data Factory、Kafka、Auto Loader、Azure Event Hubs、Databricks LakeFlow Connect
Bronze标准化表格式表达、已校验的原始数据源系统模式最小化,如过滤与添加元数据原生湖仓格式,如 Delta Lake 或 IcebergSQL、Python(DLT 等框架)
Silver清洗、具历史性且读优化的版本,仍偏向源对齐多样:镜像 Bronze、面向主题、3NF 或 Data Vault通过 SCD2 形成历史,轻量转换,对齐参考数据、特征工程等原生湖仓格式,如 Delta Lake 或 IcebergSQL、Python(dbt、Great Expectations 等)
Gold面向价值创造的优化Kimball 数据建模或 OBT跨源一致化、聚合并施加复杂业务逻辑原生湖仓格式,如 Delta Lake 或 IcebergSQL、Python(dbt、语义模型 等)

那么,是什么让 Medallion 架构对组织的数据战略如此重要?结论在于其灵活、模块化的路径,让组织能按需裁剪数据流程。虽然“三层”提供了清晰结构,但并非一刀切。关键是理解每层的长处与局限,并据此贴合实际运营与战略目标做调整。

要真正用好这种灵活分层,必须认识到全组织标准的重要性。灵活与标准并不矛盾:需要用明确的标准来引导工程团队,确保一致性有效性。鉴于层职责存在模糊与缺少统一行业定义,必须清晰界定:各层的预期产出、在每一阶段如何校验与签收数据、以及架构中各方的具体角色与职责。我们会在第四部分再度串联这些话题。

实践中,起初采用Data Vault精细模型的组织,常遭遇性能问题;而优先选择单大表(OBT)简化模型的,则容易陷入缺乏灵活性。这些经验表明:有效的数据架构需要动态权衡——在性能灵活性之间找到平衡。关键不只在“选哪个模型”,而在于允许迭代微调以兼顾当前未来诉求。为此,组织应围绕各层的期望设定清晰的数据建模标准:这样既能确保处理高效,又能在业务演进中保持可适配、可用

接下来,我们将在实践中继续探索 Medallion 架构:在落实前述最佳实践与考量的前提下,动手实现一个示例。这个实操将帮助我们更深入地理解如何在真实场景中应用 Bronze、Silver、Gold 三层,并体会数据建模与架构实现中的复杂度。下一章将从搭建所需基础设施开始,例如使用 Microsoft Fabric 等服务。