我们把第一个事务型数据转换层称为数据标准化阶段,尽管该命名可能与你组织所用的名称略有差异。一般来说,数据转换指的是将数据从一种或多种源格式转换到目标格式的程序。标准化通常是你的数据在管道中经过的诸多此类转换中的第一个。由于标准化发生在噪声、模糊性和异构性最大的入口点数据上,因此在这一步需要考虑一些特殊的挑战。
通常情况下,数据从业者都会从不同的来源收集数据,试图为他们的用户、产品或应用程序描绘一个整体的画像——其中一些是有用的,但大部分都是无用的。
以下是在数据管道的标准化点中最有可能描述数据的几个关键特征:
针对延迟进行了优化
流式端点的数据在经过优化后,可以一经创建就立即使用。如前所述,在给定固定网络性能的情况下,优化是以吞吐量为代价的,而这实际上决定了数据的完整性。这意味着不要期望批数据是完整的,因为无论其最终状态如何,它们都会立即通过数据管道进行推送。
不具层级结构的格式
经过标准化的数据可能会以不具层级结构的“平面”存储格式来进行存储,以提高效率和易用性。与干净的数据仓库+架构+表的存储方案不同,你更有可能将数据“转储”到某个中央存储库中,例如用于转换的Amazon S3存储桶。
原始文件格式
除了以“平面”形式存储外,入口点数据还可能反映流式传输的原始文件格式。我们不需要将应用程序日志数据和传感器数据转换为表格形式,这样做的成本太高了,而且大部分数据不需要经过这样的转换也可以使用。
可选数据字段
与要求每个字段都必须有值的数据仓库中的数据模式不同,JSON等原始文件数据都具有可选字段。你可能需要推断缺少该字段是什么情况——是空值,还是数据的值是0呢?是当前的时间戳,还是时间增量为0?根据存疑字段的不同,任何内容都可能是默认值,并且该字段的缺失可能会(也可能不会)成为上游处理的问题。
异构性
上述所有特征都指向某种异构性。数据来自各种不同的来源,采用各种原始的文件格式,并且与此前相同格式的数据相比,其完整性可能不同。
在数据管道的这个阶段,学习如何根据可预测的异构性来让数据有意义非常关键。要确保数据一旦被存储和处理就能轻松地进行转换,以最大化其价值。
数据仓库中的数据与数据湖中的数据:异构性版本
你可能会注意到,前面的许多功能都以数据湖格式来描述数据。回想一下我们在第2章中关于数据仓库和数据湖之间区别的讨论。数据湖通常是入口点数据的首选存储解决方案,因为它们对可接受数据类型的严格限制要小得多。这就是为什么你经常会看到流式服务(AWS Kinesis、Apache Kafka等)从不同来源收集非结构化和半结构化数据,将这些数据转储为数据湖格式,然后依靠初始级别的事务型转换将这些数据提升为数据仓库中的结构化形式。AWS Kinesis中的AWS Lambda函数或Kafka Streams的Apache Kafka消费者是应用这种标准化的典型用例。此外,如果你定期将数据移动到数据仓库中,那么像AWS Glue( oreil.ly/1TTHB )之类的转换层在这一阶段会很有帮助。
模式检查和类型强制转换是另外两种你希望在数据标准化中应用的技术。模式检查是指验证数据结构是否符合我们预期的步骤,例如是否存在必要的字段,以及它们所包含的数据是不是我们所需要的格式。类型强制转换是在数据不符合格式要求并且必须“强制”转换为新格式时去执行的过程。它们有时是显式的,有时是隐式的。在某些编程语言中,这种行为称为“casting” 。
为什么要检查模式?通常情况下,数据以某种打包格式,例如,JSON、逗号分隔值等形式。模式让我们知道第一次“解包”数据时会发生什么。改变模式是数据损坏的一个主要原因。也许由于版本更新,你曾经学习过的某个字段不再出现在那个API的调用中。也许你的工程团队为了保持一致性而重命名了一个字段,虽然显示的数据是“相同的”,但你的自定义脚本却不再有效。我们需要主动检查这类错误,这通常意味着保持对预期模式的记录,并且在它们发生变化时以某种可见的方式进行记录。
类型强制转换可能是一种更危险的数据故障形式。在某些应用程序中,类型可以隐式转换或显式转换而不会出现错误。将字符串的“4”转换为整数4没什么大不了的,但是将浮点数4.00转换为整数4呢?现在我们扔掉了有效数字,这看起来很糟糕。而更糟糕的是将浮点数4.99整型转换(integer casting)为整数4(这称为整数舍入,它不像你在数学课上学到的那样四舍五入,而是截断了小数位后的所有内容)。在某些数据应用程序中,可能需要注意类型强制转换和一般转换中的问题,它们听起来很简单,但一定会产生一些“恶意”漏洞和错误。
这一讨论将我们引向一个本质上不那么技术性,但值得任何有数据素养的人关注的区别。数据可能是模棱两可的,每个人都知道这一点,但这种模棱两可的表现形式却各不相同。句法歧义指的是数据呈现方式的混乱。也许相同的指标可能出现在数据仓库中不同字段名称下的多个位置。你同事的“clickthrough_annual”可能是你的“clickthrough_rate_yr” ,只是在一些事务型转换中被重新命名了。同样,相同的指标可能在你的数据湖中显示为整数,但在你的数据仓库中显示为浮点数。例如,始终以“.00”结尾,所以数据的值没有改变,只是它的类型改变了。这些是数据中的句法歧义,可能给数据团队带来摩擦。
更有害的是语义歧义,它指的是系统中数据用途的混淆。数据工程师可能会认为字段X存在于表中,因为该字段跟踪了一个数据管道的性能指标。而商业分析师可能会查看相同的字段,通过其模糊的名称来确定该字段跟他们感兴趣的业务目标有关,并将其添加到仪表板中。该字段在语义上是模棱两可的,因为员工无法就该字段的用途达成一致。比出现摩擦更糟糕的是,这种情况可能会导致数据错误地表示组织的关键指标。文档是避免发生此类情况的一项关键工具,而且该文档应该具有一定的前瞻性。否则歧义会以一种难以根除的方式迅速蔓延,尤其是在团队快速扩张的情况下。
虽然事务型数据转换以原始状态处理数据,但这并不意味着你需要完全盲目地运行。许多数据流和处理应用程序都提供内置警报,并能够根据需求来配置更复杂的警报。下面,我们将介绍一些流行的内置数据质量检查的具体技术示例。
AWS Kinesis
AWS Kinesis流通过AWS Lambda函数进行管理。你可以为各种预处理任务配置Lambdas,它们的泛化性允许在预处理中内置一些数据质量保证( https://oreil.ly/wZMTx )。AWS Lambda函数可以用.NET(PowerShell、C#)、Go、Java、Node.js、Python和Ruby编写,只需上传到AW S控制台即可调用。
要将AWS Lambda连接到正在运行的AWS Kinesis实例上,你将在Kinesis应用程序页面中选择“Connect to a Source”,然后选择“Record pre-processing with AWS Lambda”。在那里,你将有机会创建一个新的Lambda函数,该函数会在执行任何应用程序的SQL代码或在Amazon创建传入数据的模式快照前运行。
Apache Kafka
Apache Kafka是一个具有高学习曲线的应用程序,这意味着它为给定应用程序中的Kafka流、生产者和消费者提供了大量的颗粒度设置。Confluent、Instacluster和AWS都提供了Apache Kafka的完全托管版本,使团队更容易启动并运行强大的流式框架,并且通常可以立即处理一些必要的数据宕机预防工作。
我们要花很长时间才能详尽地研究这些问题,但可以说Apache Kafka为数据质量目的提供了大量的可配置性[事实上,作为Confluent的托管解决方案,Apache Kafka提供了一个模式注册表( https://oreil.ly/yZjfS )来支持模式检查和演变,以防止出现数据质量问题]。有关如何优化Apache Kafka以提高数据质量的更多信息,请查看项目文档( https://oreil.ly/mJ4FG )。
在默认情况下,Apache Kafka流通过JMX(Java管理扩展规范)报告流式指标。你可以使用JConsole等图形工具对JMX数据进行可视化。或者,你可以选择直接转到KafkaStreams的Java类实例并通过使用KafkaStreams#metrics()方法来访问指标。
通常来说,你在事务型转换中执行检查的优先级需要与该步骤中延迟超过吞吐量检查的优先级保持一致。换句话说,你希望在这个阶段避免进行吞吐量密集型的聚合检查,例如数据漂移。相反,将你的监控目标放在较低延迟的验证上,例如将历史模式与传入模式进行比较,或者跟踪随时间的推移所扫描的字节量的变化情况。这里进行的许多事务型“监控”甚至根本无法确保数据质量,因为它的重点是确保传入的数据不会超过现有容量、存储和内存的限制。