注意:以下翻译的准确性尚未经过验证。这是使用 AIP ↗ 从原始英文文本进行的机器翻译。
本指南旨在为开发变换的数据管道开发者提供指导。通用建议同样可能引起项目经理或平台管理员的兴趣,因为它们专注于清晰管道的高阶原则。
管道开发即软件开发
许多来自通用软件开发的最佳实践同样适用于定义组成数据管道的变换。以下是一些体现此方法的常见实践:
snake_case
编写——遵循这一约定意味着任何想引用你出色数据集的人都知道它是 awesome_dataset
,而不是 AwesomeDataset
或 Awesome_Dataset
。SNAPSHOT
事务来覆盖之前的。尝试删除数据集可能比在相同位置创建新数据集带来更多挑战。Foundry 将检查正在开发的分支上的循环依赖,但在编写代码时不会跨所有分支运行检查,例如如果存在仅在主分支上存在的 Ontology 数据输出数据集。当尝试将功能分支与包含循环依赖的分支合并时,如果在其他分支上检测到循环依赖,Foundry 仍将无法通过检查。
查看推荐的项目结构,了解组织通过多个项目的数据流的总体模型。
/Documentation
文件夹和一个 /Output
文件夹。不同的用例或工作流项目将需要更具体的名称,但始终考虑到您的项目结构和命名方案是为访客设置的指路标。/scratch
文件夹用于实验或一次性工作,而不是在个人文件夹中构建。dataset1
、dataset2
,等等)或单字母名称会使代码更难阅读和重构,并且对于新开发者来说更难接近您的工作。/raw/my_important_dataset
然后是 /clean/my_important_dataset
时,然而在许多情况下,这种命名模式会在仅显示数据集名称的视图中造成混淆。请记住,来源是被跟踪并容易看到的,所以您无需将这种“状态”嵌入到数据集的名称中。显式转换列类型: 如果您正在数据源项目中工作,即使数据连接的架构推断选择了正确的值,也要在 raw
→ clean
变换中显式转换列类型。这将有助于捕获源系统的列类型更改或无效值在同步期间导致不正确推断的破坏性更改。
对“仅时间”数据类型使用时间戳: Spark 没有仅时间数据类型用于像“10:59:00”这样的字段。为了利用 Spark 的时间戳类型附带的时间函数,将值转换为秒,然后在将其转换为时间戳之前加上 -2208988800
以将其置于第 0 年。或者,将其保留为字符串,让用户根据需要解析。
将所有数字字段转换为数值数据类型: 考虑一个飞机 ID 列,如 545
、972
、314
。将这些转换为整数列可能很诱人(毕竟它们看起来像整数,甚至可能在源系统中是整数)。然而,这有显著的缺点:
545.0
、1,234
,右对齐)。在不同时区存储时间戳: — Spark 时间戳是时区无关的。它们在内部以 UTC(也称为 Zulu,GMT)存储;显示时区预计由前端完成。
from_unixtime()
函数 ↗ 在适当的时区存储字符串。to_utc_timestamp()
函数 ↗ 将其规范化为 UTC。在提交中注释掉代码: 在进行更改时,可能很诱人地注释掉旧代码并保留以供参考或在需要时回滚。您可以在迭代时使用注释,但不要提交有注释语句的代码。这样做会产生冗余并减少可读性。旧代码很容易在先前的提交中找到。
带有作者详细信息和日期的注释: 这些信息会在提交/git 存储库中自动跟踪。手动注释容易不被更新并产生冗余。
过于冗长的注释: 注释应分享决策背后的理由而不是解释逻辑本身。力求编写“自文档化”代码;如果一组语句难以理解,这是一个明确的重构和简化信号。
保护 master
分支: 如果您正在与团队一起开发,或者即使只是在一个长期存在的个人项目上工作,保护 master 分支并实践 GitFlow ↗ 或您首选的开发工作流程。关键概念是确保移动到 master 的代码是经过审查和测试的。
编写提交消息: 提交消息是存储库中所有活动的日志。花时间写一个有用的更改描述:
修剪分支: 在长期存在的存储库中,分支可能会积累。如果分支的开发被放弃,特别是如果一个分支被合并到另一个中,通过删除分支保持整洁。这有助于了解哪些分支正在积极开发。
升级存储库: 当提示时,按照步骤升级存储库中的语言包。此过程将打开一个到包含升级的活动分支的拉取请求。您应随时在升级分支上运行管道的构建,以确保没有版本提升影响您的代码。然而,保持这些升级的最新状态通常可以确保您不会遇到其他地方看到的边缘情况,这些情况在升级版本中已修补。
实践代码审查: 当您与队友合作开发变换时,在拉取请求过程中实施一些代码审查实践。我们分享了关于代码审查最佳实践 ↗ 的想法,许多概念同样适用于审查数据变换代码。
在存储库之间共享代码: Foundry 中的存储库在项目级别操作出于多种原因,但通常有逻辑可以跨管道重用,这有几个优点:
根据 DRY 的通用代码重用。
避免在数据基础的不同区域之间分叉/不一致的逻辑。
可能有管道理想情况下使用基础管道但具有更严格的 SLA 或性能要求。在这种情况下,解决方案通常是共享逻辑而不是变换/数据集,以便关键管道可以依赖预筛选数据集、更少的变换、具有不同的构建计划等。
代码存储库是实现这一目标的绝佳方式。例如,在使用 Python 变换时,库可以将自己发布到 Conda 渠道并允许其他存储库使用它们。请参阅有关共享 Python 库的文档。
共享存储库的一个额外优势是语义版本控制。共享存储库可以用版本标记其提交(例如 1.0.0、1.0.1、2.0.0),使用库可以选择如何获取新版本。例如,存储库可能选择获取最新版本(上述 2.0.0)或仅获取特定版本(例如 1.0.1),并推迟获取新版本,直到他们手动决定这样做。后一种情况在管道至关重要且管道所有者希望有机会选择加入/批准对共享存储库的更改时特别有价值。
发布: 同样地,如果团队希望为管道提供明确的发布计划,一种选择(避免使用暂存实例或长期存在的开发分支)是将逻辑提取到共享存储库中的函数中,并使用语义版本控制将使用存储库保持在主要版本,如 1.0.0、2.0.0,甚至 0.0.0。这样,开发者可以继续迭代逻辑并标记中间版本,而无需在 master 上上线。此外,在使用存储库的分支上,开发者始终可以选择中间版本,只要他们不在发布日期之前将它们合并到 master 即可。
单元测试是一种流行的提高和维护代码质量的方法。在单元测试 ↗中,软件的小而离散的组件(“单元”)以独立、独立和自动化的方式进行测试。
Python 单元测试: 您可以通过遵循 Python 单元测试说明 在 Python 变换存储库的 CI 检查中启用 pytest
单元测试。
Java 单元测试: Java 变换单元测试的配置步骤可以在 Java 单元测试文档中找到。
单元测试应:
检查您的数据健康状况: 通常,一旦管道的一部分完成,很容易设置计划并将其置之不理。然而,即使您的逻辑是可靠的,传入的数据也可能会以影响构建的方式改变,导致性能下降、数据规模增加或直接构建失败。配置基本的数据集大小和构建时间检查,即使您没有配置警报,也会提供这些关键指标随时间变化的视图,因此您可以观察到,例如数据集大小的增长率或数据集的平均构建时间。阅读有关可用的具体健康检查的信息以及如何配置它们。
扩展健康检查: 在大多数情况下,默认的健康检查配置应该足够。如果您需要进一步的灵活性,考虑在管道中添加一个或多个派生的 健康检查
数据集。此数据集的变换可以执行任意逻辑以确定输入数据集(您正在验证的数据集)的有效性,然后输出格式化的数据,以便简单的健康检查,如 允许值
,可以报告数据集是否有效。
为该数据集设置一个计划,以便在输入数据集更新时进行构建,您将拥有一组额外的全面健康检查。
查看调度最佳实践。