《分析流处理的简要介绍》
Introduction to Stream Processing Analysis
构建可靠分布式系统的架构基础。
流处理的基础
基础是构建结构的不可动摇、不可破坏的基础。在构建成功的数据架构时,数据是整个系统的核心要素和基础的主要组成部分。
鉴于数据现在通过流处理平台(如Apache Kafka和Apache Pulsar)流入我们的数据平台的常见方式,我们(作为软件工程师)必须确保提供卫生能力和摩擦力小的防护措施,以减少与数据质量相关的问题空间,尤其是在数据进入这些快速流动的数据网络之后。这意味着建立围绕数据模式(类型和结构)、字段可用性(可为空等)和字段类型有效性(预期范围等)的api级别契约成为我们数据基础的关键基础,特别是考虑到当今现代数据系统的分散化、分布式流式特性。
然而,要达到可以建立盲目信任或高可信任数据网络的程度,我们首先必须建立智能的系统级设计模式。
构建可靠的流式数据系统
作为软件和数据工程师,构建可靠的数据系统是我们的职责,这意味着数据停机时间应该像业务的任何其他组件一样进行衡量。您可能曾听说过SLA、SLO和SLI这些术语。简而言之,这些缩写与我们对端到端系统进行评估的“合同”、 “承诺” 和 “实际措施” 相关。作为服务的所有者,我们将对我们的成功和失败负责,但少量的前期努力会产生长远的影响,从运营角度捕获的元数据不仅可以提供有关数据流畅性的有价值信息,还可以减少对数据静态时的问题解决的工作量。
采用所有者的心态
例如,服务级别协议(SLA)在您的团队或组织与您的客户(内部和外部)之间用于创建与您提供的服务相关的约束性合同。对于数据团队来说,这意味着基于您的服务级别目标(SLO)识别和捕获基于您的SLA的关键性能指标(KPM)。SLO是您打算根据SLA遵守的承诺,可以是任何承诺,例如几乎完美(99.999%)的服务正常运行时间(API或JDBC),或者只是对特定数据集的90天数据保留的承诺。最后,服务级别指标(SLI)是证明您正在按照服务级别合同进行运营的证据,通常以运营分析(仪表板)或报告的形式呈现。
知道我们想要达到的目标有助于制定实现目标的计划。这个旅程始于插入点(或摄取点),以及数据。具体而言,始于每个数据点的正式结构和身份。考虑到“越来越多的数据通过流处理平台(如Apache Kafka)进入数据平台”,拥有编译时的保证、向后兼容性和对这些数据流发出的数据进行快速二进制序列化是有帮助的。数据问责制本身可能是一个挑战。让我们看看为什么。
管理流式数据问责制
流式系统全年无休地运行,每天24小时,每周7天。如果没有对问题应用正确的前期努力,这可能会使事情变得复杂,而其中一个问题不时出现的问题是数据损坏,即在传输过程中出现的数据问题。
处理传输中的数据问题
有两种常见的方法可以在数据传输过程中减少数据问题。首先,您可以在数据网络的边缘引入门禁,使用传统的应用程序编程接口(API)进行数据的协商和验证。其次,您可以创建和编译助手库,或者软件开发工具包(SDK),以强制执行数据协议,并使分布式写入者(数据生产者)进入流数据基础设施。您甚至可以同时使用这两种策略。
数据门禁
在数据网络的边缘添加网关API的好处是,您可以在数据生产点强制进行身份验证(该系统是否可以访问此API?),授权(该系统是否可以向特定数据流发布数据?)和验证(此数据是否可接受或有效?)。下图1-1显示了数据网关的数据流。
数据网关服务充当您受保护的(内部)数据网络的数字门卫。其主要角色是在边缘(请参见上图1-1中的API /服务)控制、限制甚至限制未经身份验证的访问,通过授权允许上游服务(或用户)发布数据(通常通过使用服务ACL来处理),并提供身份验证(考虑服务身份和访问IAM,Web身份和访问JWT,以及我们的老朋友OAUTH)。
网关服务的核心责任是在发布潜在损坏或通常错误的数据之前验证传入数据。如果网关能够正确执行其工作,只有“好”数据才会通过并进入数据网络,而这是事件和操作数据通过流处理进行消化的通道,换句话说:
“这意味着产生数据的上游系统在生成数据时可能会快速失败。这样可以防止损坏数据进入数据网络的流式或静态数据管道,并通过错误代码和有帮助的消息以一种更自动化的方式与生产者建立对话,确切地了解为什么以及出了什么问题。”
使用错误消息提供自助解决方案
好的体验和糟糕的体验之间的区别在于从糟糕到好所需的努力。我们可能都曾使用过、或在…上工作过、或听说过那些毫无道理地失败的服务(空指针异常随机抛出500)。
为了建立基本的信任,稍微做一些工作就会走得很远。例如,从API端点返回一个HTTP 400,并附带以下消息体(如下所示)
{ "error": { "code": 400, "message": "事件数据缺少userId,并且时间戳无效(应为ISO8601格式的字符串)。请查看http://coffeeco.com/docs/apis/customer/order#required-fields上的文档以调整有效负载。" }}
提供了400的原因,并使我们(作为服务拥有者)发送数据的工程师能够在不设置会议、引爆警报器或在Slack上联系每个人的情况下解决问题。当有机会时,请记住每个人都是人,我们喜欢闭环系统!
API数据的优缺点
这种API方法有其优点和缺点。
优点是大多数编程语言都可以直接使用HTTP(或HTTP/2)传输协议 – 或者通过添加一个小型库 – 而JSON数据几乎是当今最通用的数据交换格式。
另一方面(缺点),可以说对于任何新的数据域,都需要编写和管理另一个服务,并且没有某种形式的API自动化或遵循像OpenAPI这样的开放规范,每个新的API路由(端点)所需的时间比必要的时间更长。
在许多情况下,未能及时提供数据摄取API的更新,或者在扩展和/或API停机时间、随机故障或人们不进行沟通方面出现问题,都为绕过“愚蠢”的API并尝试直接将事件数据发布到Kafka提供了必要的理由。虽然API可能会感觉阻碍了进展,但保持一个共同的门卫是有充分理由的,特别是在数据质量问题(如损坏的事件或意外混合的事件)开始破坏流媒体梦想之后。
为了解决这个问题(并几乎完全消除它),良好的文档、变更管理(CI/CD)以及一般的软件开发卫生习惯,包括实际的单元测试和集成测试,可以实现快速的功能和迭代周期,而不会降低信任。
理想情况下,数据本身(模式/格式)可以通过启用字段级验证(谓词)、生成有用的错误消息并为自身利益行事来规定其自己的数据级契约的规则。嘿,通过一些路由或数据级元数据以及一些创造性思维,API可以自动生成自我定义的路由和行为。
最后,网关API可以被视为集中的麻烦制造者,因为上游系统未能发出有效数据(例如被门卫阻止),导致有价值的信息(事件数据、指标)被丢弃。在这里,责任的问题也往往是双向的,因为门卫的错误部署可能使上游系统变得盲目,而这些系统在网关停机时没有设置重试(即使只有几秒钟)。
撇开所有的利弊不谈,使用网关API在数据进入数据平台之前停止传播损坏的数据意味着当问题发生时(因为问题总是会发生),问题的范围被减小到一个给定的服务。这肯定比调试分布式的数据管道、服务和各种最终数据目的地和上游系统,以找出坏数据是由公司中的“某个人”直接发布的要好。
如果我们去掉中间人(网关服务),那么管理“预期”数据传输的能力就落在“库”(以专门的SDK形式)的手中。
软件开发工具包(SDK)
SDK是导入到代码库中以简化操作、活动或其他复杂操作的库(或微框架)。它们也被称为另一个名字,客户端。以前关于使用良好的错误消息和错误代码的示例为例。为了通知客户端其先前的操作无效,这个过程是必要的,但是将适当的防护措施直接添加到SDK中以减少任何潜在问题的范围可能是有利的。例如,假设我们设置了一个API来跟踪客户的与咖啡相关的行为。
使用SDK防护措施减少用户错误
客户端SDK理论上可以包含管理与API服务器的交互所需的所有工具,包括身份验证、授权以及验证方面,如果SDK完成了其工作,验证问题将不复存在。下面的代码片段显示了一个示例SDK,该示例SDK可用于可靠地跟踪客户事件。
import com.coffeeco.data.sdks.client._import com.coffeeco.data.sdks.client.protocol._Customer.fromToken(token) .track( eventType=Events.Customer.Order, status=Status.Order.Initalized, data=Order.toByteArray )
通过一些额外的工作(即客户端SDK),数据验证或事件损坏的问题几乎可以完全消失。SDK本身可以处理其他问题,例如在服务器离线的情况下如何重试发送请求。SDK不必立即重试所有请求,或者在某个循环中无限期地向网关负载均衡器发送请求,而是可以采取更智能的操作,例如指数退避。有关出现问题时会发生什么错误的详细信息,请参阅“雷鸣的兽群问题”!
雷鸣的兽群问题假设我们有一个单独的网关API服务器。您编写了一个很棒的API,公司中的许多团队都向该API发送事件数据。一切都很顺利,直到有一天,一个新的内部团队开始向服务器发送无效数据(并且他们没有尊重您的HTTP状态代码,而是将所有非200的HTTP代码视为重试的原因。但是等等,他们忘记添加任何重试启发式,例如指数退避,因此所有请求都会无限期地重试-在一个不断增长的重试队列中)。请注意,在这个新团队加入之前,从来没有理由运行多个API服务器实例,也从来没有必要使用任何服务级别的速率限制器,因为一切都在约定的服务级别协议内顺利运行。
</
嗯,那是在今天之前的事情了。现在您的服务已经离线。数据正在备份,上游服务正在填充队列,人们因为您的单点故障而感到不满……所有这些问题都源自一种资源匮乏的形式,被称为“雷鸣般的牛群问题”。当许多进程正在等待事件发生时,例如系统资源可用或在本例中,API服务器恢复在线时,就会出现此问题。现在,所有进程都竞争尝试获取资源,而在许多情况下,单个进程(API服务器)的负载足以使服务再次离线。不幸的是,这将重新开始资源匮乏的循环。当然,除非您能够使牛群镇静下来或将负载分散到更多的工作进程上,从而降低网络上的负载,以便资源再次有空间呼吸。虽然上面的初始示例更像是无意中的分布式拒绝服务攻击(DDoS),但可以通过客户端(指数退避或自节流)和API边缘(负载均衡和速率限制)来解决这些问题。
最终,如果没有正确的监控指标、监控和系统级(SLAs/SLIs/SLOs)警报的帮助,数据可能会消失,这可能是一个难以解决的挑战。
无论您决定在数据网络边缘添加数据网关API、使用自定义SDK进行上游一致性和问责制,还是在处理将数据输入数据平台时采用其他方法,了解您的选择是很好的。无论数据通过哪种路径发出到数据流中,介绍流数据都不完整,如果不适当地讨论数据格式、协议以及二进制可序列化数据的主题,我们可能会发现更好的处理数据问责问题的方法!
选择适合工作的正确数据协议
当您想到结构化数据时,首先想到的可能是JSON数据。JSON数据具有结构,是一种标准的基于网络的数据协议,而且最重要的是它非常容易使用。这些都是快速入门的好处,但随着时间的推移,如果没有适当的保护措施,您可能会在将JSON用于流式系统时遇到问题。
与JSON的爱恨关系
第一个问题是JSON数据是可变的。这意味着作为数据结构,它是灵活的,因此也是脆弱的。要使数据有问责制,数据必须是一致的,并且在网络传输中(在网络上)序列化格式(二进制表示)应该高度可压缩。对于JSON数据,您必须为有效载荷中表示的每个对象的所有字段发送键。不可避免地,这意味着对于一系列对象中的每个附加记录(除第一个记录外),您通常会发送大量的额外数据。
幸运的是,这不是一个新问题,而且碰巧有对这些问题的最佳实践,以及关于哪种策略是最佳的优化序列化数据的多种思路。这并不是说JSON没有优点。只是当建立坚实的数据基础时,结构越多越好,压缩级别越高越好,只要不会消耗大量CPU周期。
可序列化结构化数据
当涉及到高效编码和传输二进制数据时,两个序列化框架经常被提及:Apache Avro和Google Protocol Buffers(protobuf)。这两个库提供了CPU高效的技术来序列化基于行的数据结构,并且除了这两种技术之外,它们还提供了自己的远程过程调用(RPC)框架和功能。让我们先看看Avro,然后是protobuf,最后我们将看看远程过程调用。
Avro消息格式
使用Avro,您可以使用记录的概念为结构化数据定义声明性模式。这些记录只是以JSON格式的数据定义文件(模式)存储在avsc文件类型中。以下示例显示了Avro描述符格式中的Coffee模式。
{ "namespace": "com.coffeeco.data", "type": "record", "name": "Coffee", "fields": [ {"name": "id", "type": "string"}, {"name": "name", "type": "string"}, {"name": "boldness", "type": "int", "doc": "从浅到深,1到10"}, {"name": "available", "type": "boolean"} ]}
使用avro数据可以采用两种不同的路径,与您在运行时想要如何工作相关。您可以采用编译时方法,或者在运行时根据需要进行事物处理。这使得灵活性能够提升交互式数据发现会话。例如,avro最初是作为一种高效的数据序列化协议而创建的,用于在Hadoop文件系统中作为分区文件长期存储大型数据集合。由于数据通常从一个位置读取,并写入到另一个位置,在HDFS中,avro可以每个文件存储一次(在写入时使用的)模式。
Avro二进制格式
当将一组avro记录写入磁盘时,该过程会将avro数据的模式直接编码到文件中(一次)。在编码Parquet文件时,也有类似的过程,其中模式被压缩并作为二进制文件页脚写入。我们在第4章结束时亲眼见证了这个过程,当时我们通过为StructType添加StructField级别的文档,对我们的DataFrame进行编码。该模式用于编码我们的DataFrame,并且在写入磁盘时保留了我们的内联文档,以便在下一次读取时使用。
启用向后兼容性和防止数据损坏
在读取多个文件作为单个集合时,如果记录之间的模式发生变化,可能会出现问题。Avro将二进制记录编码为字节数组,并在反序列化时(从字节数组转换回对象)对数据应用模式。
这意味着您需要额外的预防措施来保持向后兼容性,否则您可能会遇到ArrayIndexOutOfBounds异常的问题。
这可能是由于模式以微妙的方式发生了变化。例如,假设您需要将整数值更改为长整数值,用于模式中的特定字段。不要这样做。这将由于从int到long的字节大小增加而破坏向后兼容性。这是由于使用模式定义为记录的每个字段在字节数组中的起始和结束位置。为了保持向后兼容性,您需要弃用整数字段的使用(同时在avro定义中保留它),并在模式中添加(追加)一个新字段以供将来使用。
流式Avro数据的最佳实践
从具有有用的嵌入式模式的静态avro文件转移到无界流式二进制数据时,主要的区别是您需要自己提供模式。这意味着您需要支持向后兼容性(在需要在模式更改之前和之后回放和重新处理数据的情况下),以及支持向前兼容性,以防您已有的读取器已经从流中消耗。
挑战在于支持这两种兼容性形式,因为avro无法忽略未知字段,这是支持向前兼容性的要求。为了支持这些挑战,Confluence的工作人员开源了他们的模式注册表(用于与Kafka一起使用),该注册表可以在Kafka主题(数据流)级别上对模式进行版本控制。
如果不使用模式注册表支持avro,您将需要确保已更新任何活动的读取器(Spark应用程序或其他)以使用新版本的模式,然后再更新写入器的模式库版本。否则,一旦切换,您可能会发现自己发生故障。
Protobuf消息格式
使用protobuf,您使用消息的概念定义结构化数据定义。消息采用的格式更像是在C语言中定义结构体。这些消息文件被写入具有.proto文件扩展名的文件中。Protocol Buffers的优点是可以使用导入。这意味着您可以定义常见的消息类型和枚举,可以在大型项目内使用,甚至可以导入到外部项目中,实现广泛的重用。使用protobuf创建Coffee记录(消息类型)的简单示例。
syntax = "proto3";
option java_package="com.coffeeco.protocol";
option java_outer_classname="Common";
message Coffee {
string id = 1;
string name = 2;
uint32 boldness = 3;
bool available = 4;
}
使用protobuf,您只需定义一次消息,然后编译成您选择的编程语言。例如,我们可以使用ScalaPB项目(由Nadav Samet创建和维护)的独立编译器从coffee.proto文件生成Scala代码,或者利用Buf的优势,Buf创建了一套围绕protobuf和grpc的工具和实用程序。
代码生成
编译protobuf可以实现简单的代码生成。以下示例取自/ch-09/data/protobuf目录。章节README中的指示说明了如何安装ScalaPB并包括设置正确的环境变量以执行该命令的步骤。
$SCALAPBC/bin/scalapbc -v3.11.1 \ --scala_out=/Users/`whoami`/Desktop/coffee_protos \ --proto_path=$SPARK_MDE_HOME/ch-09/data/protobuf/ \ coffee.proto
这个过程可以节省时间,因为它使您不必编写额外的代码来序列化和反序列化您的数据对象(跨语言边界或不同代码库之间)。
Protobuf二进制格式
序列化(二进制传输格式)使用二进制字段级分隔符进行编码。这些分隔符用作标记,用于标识包含在序列化的protobuf消息中的数据类型。在coffee.proto示例中,您可能注意到每个字段类型旁边有一个索引标记(string id = 1;),这用于帮助在传输过程中对消息进行编码/解码。这意味着与avro二进制相比有一些额外的开销,但如果您阅读编码规范,您会发现其他效率远远超过任何额外的字节(例如位打包,有效处理数值数据类型和对每个消息的前15个索引的特殊编码)。就将protobuf用作流式数据的二进制协议选择而言,它的优点远远超过了缺点。它以支持向后和向前兼容性的方式之一,使其不仅可以弥补自己,而且可以超越自己。
启用向后兼容性和防止数据损坏
修改protobuf模式时需要记住类似的规则,就像我们讨论avro时一样。作为一个经验法则,您可以更改字段的名称,但不能更改类型或更改位置(索引),除非您想破坏向后兼容性。当涉及支持长期数据的任何类型时,这些规则可能会被忽视,并且随着团队在使用protobuf方面的熟练程度提高,这可能会变得特别困难。如果您不小心,这种需要重新安排和优化的需求可能会给您带来麻烦。(有关更多上下文,请参阅下面称为“随时间维护数据质量”的提示)。
流式传输Protobuf数据的最佳实践
由于protobuf支持向后和向前兼容性,这意味着您可以部署新的写入器而无需担心首先更新读取器,而且读取器也是如此,您可以使用更新版本的protobuf定义更新它们,而无需担心所有写入器的复杂部署。protobuf使用未知字段的概念来支持向前兼容性。这是avro规范中不存在的附加概念,它用于跟踪由于本地版本的protobuf与当前正在读取的版本之间的差异而无法解析的索引和相关字节。有益的是,您也可以随时选择使用protobuf定义中的较新更改。
例如,假设您有两个流式应用程序(a)和(b)。应用程序(a)正在处理来自上游Kafka主题(x)的流式数据,增强每个记录的附加信息,然后将其写入新的Kafka主题(y)。现在,应用程序(b)从(y)读取并执行它的操作。假设有一个较新版本的protobuf定义,而应用程序(a)尚未更新到最新版本,而上游Kafka主题(x)和应用程序(b)已经更新并希望使用升级中提供的一些新字段。令人惊奇的是,仍然可以将未知字段通过应用程序(a)传递到应用程序(b),而甚至不知道它们的存在。
请参阅“Tips for maintaining good data quality over time”以获取更深入的了解。
提示:保持数据质量
在使用avro或protobuf时,您应该将模式视为要推送到生产环境的代码。这意味着创建一个可以提交到您公司的github(或您使用的任何版本控制系统)的项目,还意味着您应该为模式编写单元测试。这不仅提供了如何使用每个消息类型的实时示例,而且测试数据格式的重要原因是确保模式的更改不会破坏向后兼容性。更好的是,为了对模式进行单元测试,您需要首先编译(.avsc或.proto)文件并使用相应的库代码生成。这使得创建可发布的库代码更容易,并且您还可以使用发布版本(版本1.0.0)来记录对模式的每一次更改。
一种简单的方法是在项目生命周期中将每个消息的二进制副本进行序列化和存储,跨越所有模式更改。我发现直接将此步骤添加到单元测试中非常成功,使用测试套件直接在项目测试资源目录中创建、读取和写入这些记录。这样,所有模式更改的每个二进制版本都可以在代码库本身中使用。
通过一点额外的前期工作,您可以在整个大计划中节省很多麻烦,并且可以在晚上放心地知道您的数据是安全的(至少在生产和消费方面)
使用Buf工具和Spark中的Protobuf
自2021年编写本章以来,Buf Build(https://buf.build/)已经成为了一家全面支持protobuf的公司。他们的工具易于使用、免费开源,并在恰当的时间出现,为Spark社区的一些初始工作提供了动力。Apache Spark项目在Spark 3.4中引入了对Protocol Buffers的完全本机支持,以支持spark-connect,并使用Buf来编译GRPC服务和消息。毕竟,Spark Connect是一个在JVM之外嵌入Spark应用程序的GRPC本机连接器。
传统的Apache Spark应用程序必须在某个地方作为驱动程序应用程序运行,过去这意味着使用pyspark或本机spark,这两种情况下仍然在JVM进程之上运行。
归根结底,Buf Build在构建过程中提供了安心。为了生成代码,只需运行一个简单的命令:buf generate
。对于简单的linting和一致的格式化,buf lint && buf format -w
。然而,最重要的是检测破坏性更改。buf breaking --against .git#branch=origin/main
只需这样就可以确保新的消息定义变化不会对当前正在运行的任何内容产生负面影响。*将来,我将撰写一篇关于如何使用buf进行企业分析的文章,但现在,是时候结束本章了。
那么我们在哪里。您现在知道在制定长期数据问责战略时使用avro或protobuf有益处。通过使用这些语言无关的、基于行的、结构化数据格式,您减少了长期语言锁定的问题,为以后的编程语言留下了空间。因为老实说,支持遗留的库和代码库可能是一项不感激的任务。此外,序列化格式有助于减少发送和接收大量数据时的网络带宽成本和拥塞。这还有助于减少保留长期数据的存储开销。
最后,让我们看看这些结构化数据协议如何在使用远程过程调用在网络上发送和接收数据时提供额外的效率。
远程过程调用
RPC框架简而言之,通过传递序列化的消息来实现客户端应用程序通过本地函数调用透明地调用远程(服务器端)方法(过程)。客户端和服务器端的实现使用相同的公共接口定义来定义可用的功能性RPC方法和服务。接口定义语言(IDL)定义了协议和消息定义,并充当客户端和服务器端之间的合同。让我们通过查看流行的开源RPC框架gRPC来看看它的运作。
gRPC
gRPC最初在Google概念化和创建,它代表“通用”远程过程调用,是一个强大的开源框架,被用于高性能服务,从分布式数据库协调(例如CockroachDB)到实时分析(例如Microsoft Azure视频分析)。
图9-3展示了gRPC的示例。服务器端的代码使用C++编写以提高速度,而使用ruby和java编写的客户端可以通过protobuf消息进行互操作。
使用协议缓冲区来定义消息、序列化以及声明和定义服务,gRPC可以简化数据捕获和服务构建的过程。例如,假设我们想继续创建一个用于跟踪客户咖啡订单的API的练习。API合同可以在一个简单的服务文件中定义,然后可以使用相同的服务定义和消息类型构建服务器端实现和任意数量的客户端实现。
定义gRPC服务
您可以轻松地定义服务接口、请求和响应对象,以及客户端和服务器之间需要传递的消息类型,就像1-2-3一样简单。
syntax = "proto3";service CustomerService { rpc TrackOrder (Order) returns (Response) {} rpc TrackOrderStatus (OrderStatusTracker) returns (Response) {}}message Order { uint64 timestamp = 1; string orderId = 2; string userId = 3; Status status = 4;}enum Status { unknown_status = 0; initalized = 1; started = 2; progress = 3; completed = 4; failed = 5; canceled = 6;}message OrderStatusTracker { uint64 timestamp = 1; Status status = 2; string orderId = 3;}message Response { uint32 statusCode = 1; string message = 2;}
通过引入gRPC,实现和维护数据基础架构中使用的服务器端和客户端代码可以变得更加容易。由于protobuf支持向前和向后的兼容性,这意味着旧的gRPC客户端仍然可以向新的gRPC服务发送有效的消息,而不会遇到常见的问题和痛点(在“数据传输中的数据问题”中讨论过)。
gRPC使用HTTP/2通信
作为一个额外的好处,gRPC能够使用HTTP/2作为其传输层。这也意味着您可以利用现代数据网格(如Envoy)进行代理支持、路由和服务级身份验证,同时还减少了标准HTTP over TCP中出现的TCP数据包拥塞问题。
在解决数据传输中的问题和实现数据的责任性方面,从数据中心点向外扩展。在深入了解流式数据之前,将数据输入到数据网络中的过程应该被视为必备条件。
概要
本文的目标是在我们盲目从传统(静止)的基于批处理的思维方式转向理解实时流数据的风险和回报之前,提供所需的移动部件、概念和背景信息。
实时利用数据可以带来快速、可行动的见解,并为最先进的机器学习和人工智能打开大门。
然而,分布式数据管理如果没有考虑到正确的步骤,也可能会变成数据危机。请记住,如果没有建立在有效(可信赖)数据之上的坚实数据基础,那么实时之路将不会是一项简单的任务,而是会有许多颠簸和弯路。
我希望您喜欢第9章的下半部分。要阅读本系列的第一部分,请前往“分析流处理的简要介绍”。
分析流处理的简要介绍
为工程师和其他人构建心智模型
towardsdatascience.com
如果您想更深入地了解,请查看我的书,或者用高五的方式支持我。
Apache Spark的现代数据工程:构建关键任务流的实践指南
Amazon.com:Apache Spark的现代数据工程:构建关键任务流的实践指南
www.amazon.com
如果您可以访问O’Reilly Media,那么您也可以完全免费阅读本书(对您有利,对我不利),但如果有机会,请在某个地方免费找到这本书,或者获取一本电子书以节省运费(或者不需要找地方放一个600+页的书)。
Apache Spark的现代数据工程:构建关键任务流的实践指南
在现代数据工程生态系统中利用Apache Spark。这本实践指南将教你如何编写完整的…
learning.oreilly.com