在 Dropbox,我们将有关人们若何利用我们的产物和办事的数据存储在基于 Hadoop 的数据湖中。各类团队都依赖那个数据湖中的信息来实现各类营业目标——例如,阐发、计费和开发新功用——我们的工做是确保只要高量量的数据才气抵达湖中。

我们的数据湖大小超越 55 PB,在处置那种规模的数据时,量量始末是一个大问题。我们构建的功用、我们做出的决策以及我们陈述的财政成果都取决于我们的数据能否准确无误。但是因为要挑选的数据如斯之多,量量问题可能会十分难以发现——即便我们一起头就晓得它们存在的话。那相当于在暗中的房间里寻找黑猫的数据工程。

过去,Dropbox 的差别团队接纳差别的办法来验证数据,接纳差别的尺度和差别的管道。不成制止地,当我们在数据中发现错误时,我们晓得可能还有更多的人没有被发现。那就是为什么 Dropbox 在 2018 年创建了一个专门的数据工程团队来监视我们数据湖中数据的验证,并试图在那些问题发作之前发现它们。

所有以我们规模运营的组织都必需考虑那些数据验证问题。事实上,数据验证并非一个能够间接处理的问题。有些系统没有数据量量查抄,而其他系统则测验考试在利用数据之前查抄所有内容。两者都有他们的问题。

若是我们的查抄过于宽松,Dropbox 用户能够更快地利用数据,但量量可能会很差。另一方面,若是我们的查抄过于严酷,数据的量量会更好,但需要更多的时间和资本来处置——当数据筹办好时,它可能已颠末时而不再有用了。

在 Dropbox,我们的数据验证框架介于两者之间。那个故事将详细介绍我们若何在我们的大数据管道中施行新的量量查抄系统,以实现简单性和笼盖范畴的准确平衡——供给高量量的数据,而不会形成没必要要的困难或高贵的维护。

笼盖范畴、代码和设置装备摆设决策

数据量量问题能够有多种形式。查询能否返回反复数据?必填字段中能否贫乏数据?预期美圆金额为负数吗?若是一条数据上的时间戳在将来或过去太远怎么办?我们晓得我们想要一个不只易于设置装备摆设,并且可以在需要时施行各类量量查抄的系统。

我们查看了 GitHub 上的开源项目,也考虑了贸易产物。Great ExPEctation s和dbt是我们考虑的两个选项。两者都撑持 NOT NULL、上限/下限和 UNIQUE 等简单查抄,并将那些查抄存储在 YAML 文件中。但是关于更复杂的查抄,例如百分比或复杂的营业逻辑,我们仍然需要编写 SQL。Great Expectations 和 dbt 也是独立的办事而不是库,那使得它们很难与我们的编排系统 Airflow 集成。例如,固然我们能够从 Airflow 中挪用 dbt,获取成果并将其返回给 Airflow,但任何失败都需要我们利用 dbt 的接口停止查询拜访。

我们还查看了普遍利用的库Evidently,但觉得它关于我们想要交付的工具来说太复杂了,并且需要大量的 Python。当我们找不到完全满足我们需求的任何工具时,我们决定从头起头构建一个处理计划。

我们必需做出的第一个决定是关于笼盖范畴。换句话说,我们的测试要涵盖哪些类型的问题?一方面,我们希望我们的系统可以验证任何复杂的数据。另一方面,我们晓得我们无法八面玲珑。所以我们决定接纳 80/20 的办法。那一原则表白,给定场景中 80% 的所有输出仅来自 20% 的输入,我们揣度我们能够仅通过少量测试来处理最常见的数据量量问题。我们的办法未涵盖的问题可能永久不会呈现,而那些确实呈现的问题能够按照详细情况停止处置。

接下来,我们必需决定 利用哪种编程语言来编写我们的验证框架。我们的数据工程师在 SQL、Java、Scala、SchemaPLT、Python 和 C 等语言方面拥有丰硕的经历,而且各有利弊。但是颠末多方讨论,我们选择了SQL。为什么选择那么原始的语言?好吧,每小我都晓得 SQL!它足够灵敏,能够处置简单和复杂的验证,而且相关于其他语言,任何级此外工程师都能够轻松地停止开发和维护。

最初,我们需要决定在哪里存储定义数据验证查抄的规则。在那里我们没有太多选择。数据库被普遍用于此目标,而且具有易于拜候和设置装备摆设的长处。但是将我们的规则存储为代码将使审查更改和查看我们的修订汗青变得愈加容易。最初我们选择将所有验证做为代码存储在 Git 中——因为只要数据工程师才晓得数据库会招致几问题。

够简单吗?是的。够有效吗?是的!

施行我们的验证 SQL

我们利用 Airflow 编排系统将新数据放入湖中。为了施行我们的框架,我们向 Airflow 添加了一个验证运算符,用于在摄取新数据后施行我们的验证 SQL。出于性能原因,我们对所有验证利用一个查询。那个查询的成果只要一行良多列,每一列代表一次数据校验操做。若是一列包罗零,则暗示验证通过。任何其他值暗示验证失败。

Airflow 还允许我们将多个源表组合成一个表,验证成果,然后将该成果与其他表组合以停止进一步验证。那个逻辑让我们能够起头搜集更多关于验证失败的细节——例如,有问题的行的百分比——而无需任何额外的步调。我们的框架搜集所有非零值,将 Airflow 使命标识表记标帜为失败,并通过 Airflow 抛出异常。然后我们向任何待命的人发送 PagerDuty 警报。

例如,若是我们为another_column得到了不测数量的 NULL和可疑的低数据,我们将得到以下异常:

Failed validations: suspiciously_low_data: 999999, high_null_perc_another_column: 0.07

按照异常动静,数据工程师能够领会失败的原因以及应该查抄哪些数据。通过对该框架停止细小的更改,我们以至能够在需要时返回字符串——例如,不测的列举值或电子邮件域。

以下是那些查抄在理论中的一些示例。

上游数据问题

查抄“user”表中能否有任何数据:

SELECT CASE WHEN COUNT(*)=0 THEN 1 ELSE 0 END AS is_no_data FROM "user"

查抄我们在强迫列中没有 NULL:

SELECT SUM(CASE WHEN email IS NULL THEN 1 ELSE 0 END) AS email_nulls FROM "user"

查抄反复的 ID:

SELECT SUM(CASE WHEN rows_per_user_id > 1 THEN 1 ELSE 0 END) AS duplicated_user_id FROM ( SELECT * , COUNT(*) OVER (PARTITION BY user_id) AS rows_per_user_id FROM "user" ) u阐发警报

查抄剩余的 NULL 能否低于可承受的阈值:

SELECT CASE WHEN locale_null_perc > 0.05 THEN locale_null_perc ELSE 0 END AS high_null_perc_locale FROM ( SELECT SUM(CASE WHEN locale IS NULL THEN 1 ELSE 0 END)/COUNT(*) AS locale_null_perc FROM "user" ) t营业逻辑查抄

查抄用户能否有负账户余额:

SELECT SUM(CASE WHEN u.balance <=0 AND COALESCE(p.ttl_amount, 0) < COALESCE(i.ttl_amount, 0) THEN 1 ELSE 0 END) AS users_with_incorrect_non_pos_balance FROM "user" u LEFT OUTER JOIN ( SELECT user_id, SUM(amount) AS ttl_amount FROM payment GROUP BY user_id ) p ON p.user_id = u.user_id LEFT OUTER JOIN ( SELECT user_id, SUM(amount)AS ttl_amount FROM invoice GROUP BY user_id ) i ON i.user_id = u.user_id

为“user”表筹办最末验证成果:

SELECT is_no_data , duplicated_user_id , suspiciously_low_data , email_nulls , CASE WHEN locale_null_perc > 0.05 THEN locale_null_perc ELSE 0 END AS high_null_perc_locate FROM ( SELECT CASE WHEN COUNT(*)=0 THEN 1 ELSE 0 END AS is_no_data , SUM(CASE WHEN rows_per_user_id > 1 THEN 1 ELSE 0 END) AS duplicated_user_id , CASE WHEN COUNT(*)<1000000 THEN COUNT(*) ELSE 0 END AS suspiciously_low_data , SUM(CASE WHEN email IS NULL THEN 1 ELSE 0 END) AS email_nulls , SUM(CASE WHEN locale IS NULL THEN 1 ELSE 0 END)/COUNT(*) AS locale_null_perc FROM ( SELECT * , COUNT(*) OVER (PARTITION BY user_id) AS rows_per_user_id FROM "user" ) u ) t只要好工具

我们的办法有一个很大的缺点:一旦数据进入数据湖,手艺上任何人都能够利用它。因为我们的查抄可能需要数小时才气完成——出格是若是有大量数据需要验证,或者需要运行大量验证——那意味着,在此期间,用户将可以拜候未经历证的数据。

我们本能够让我们的下流管道期待我们的验证使命完成,但那很难跟着规模的扩大而维持。那么我们若何让他人晓得哪些数据已颠末验证,哪些还没有颠末验证呢?我们的处理计划是将我们的数据验证框架与 Airflow 中现有的 SQL 施行运算契合并,从而将新数据添加到我们的数据湖中。

如今填充特定表的运算符施行两个查询:填充和验证。起首,操做员创建一个临时表,用填充查询填充该表,然后对临时表挪用验证查询。然后,若是数据通过,操做员将颠末验证的数据移至消费表。

此过程能够是可选的或强迫的,详细取决于数据,那在需要时为我们供给了更大的灵敏性。拜候未经历证的数据关于非关键查抄很有用,而强迫验证会阻遏任何下流对营业关键数据的利用。通过那种体例,我们包管只要准确的数据可供利用。

使用我们的数据验证框架平衡质量和覆盖范围(译文-来自: Dropbox)  第1张

简单的系统,大的成果

像如许的系统更大的风险是我们不克不及立竿见影。通过设想,该系统能够避免利用不准确的数据——而且因为搜集有关下流数据量量问题的统计数据需要数月时间,因而领会我们的框架能否一般工做的独一办法就是拭目以待。

发布一年多后,我们末于能够说我们的验证框架获得了庞大的胜利。与前一年比拟,我们发现数据量量事务削减了 95%,我们称之为 SEV(如 SEVerity)。鉴于我们的 80/20 办法,关于如许一个简单的系统,那些成果十分好。多亏了我们的框架,我们可以发现量量问题,例如……

不测屡次发送不异邮件的电子邮件活动一组仍然活泼的账户被错误地标识表记标帜为流失,因而不计入收入Dropbox 和 Dropbox Sign 的新订阅包被错误地计为尺度 Dropbox 订阅六个月内我们可能会错过的 24 个反复数据案例

系统的简单性不只使其易于维护,并且易于修改和扩展其功用。接下来,在我们的道路图上,我们方案为遗留管道和没有严酷验证要求但仍会从我们的量量查抄中受益的管道添加验证。我们还方案进一步构建我们的阐发才能,那将使我们可以阐发现有的管道并保举可能的验证,并在我们向数据湖添加更大都据时监控验证笼盖范畴的变革。

做者:Alexey Sanko

出处:https://dropbox.tech/infrastructure/balancing-quality-and-coverage-with-our-data-validation-framework