写点什么

【译】日志:每个软件工程师都应该了解实时数据的统一抽象【三】

作者:Rae
  • 2022 年 9 月 30 日
    浙江
  • 本文字数:4291 字

    阅读完需:约 14 分钟

【译】日志:每个软件工程师都应该了解实时数据的统一抽象【三】

非常经典的一篇博文,由于原文太长,所以按照不同的 part 进行翻译,此文是 part three。


贴心的目录在这里:

【译】日志:每个软件工程师都应该了解实时数据的统一抽象【一】

【译】日志:每个软件工程师都应该了解实时数据的统一抽象【二】


这部分内容首先指出这种日志处理的很大意义在于可以协调生产者和消费者不匹配的速度;其次,说明了其可以保持信息的有序,提供缓冲能力(其实就是生产和消费速度的协调),并提供一种状态维护的能力。最后非常简单地介绍了 Kafka 的日志压缩机制。


原文:The Log: What every software engineer should know about real-time data's unifying abstraction

发布时间:2013 年 12 月 16 日


第三部分:日志和实时流处理

到目前为止,我只描述了一种从一个地方到另一个地方复制数据的奇特方法。但是在存储系统之间交换字节并不是故事的结局。事实证明,“日志”是“流”的另一个词,日志是流处理的核心。

但是,等等,流处理到底是什么?

如果你是 90 年代末和 2000 年初数据库 文献或半成功的数据 基础架构 产品的粉丝,你可能会将流处理,与构建 SQL 引擎或用于事件驱动处理的“方框和箭头”接口,努力联系起来。

如果你关注开源数据系统的爆炸式增长,你可能会将流处理与该领域的一些系统联系起来——例如,StormAkkaS4和 Samza。但是大多数人认为这些是一种异步消息处理系统,与集群感知的 RPC 层没有什么不同(事实上,这个领域的一些东西正是如此)。

这两种观点都有些局限。流处理与 SQL 无关。它也不限于实时处理。没有内在的原因,你不能使用各种不同的语言来处理昨天或一个月前的数据流来表达计算。


我将流处理视为更广泛的东西:用于连续数据处理的基础设施。我认为计算模型可以像 MapReduce 或其他分布式处理框架一样通用,并且具有产生低延迟结果的能力。

处理模型的真正驱动力是数据收集的方法。批量收集的数据自然是批量处理的。当数据被连续收集时,它自然会被连续处理。

美国人口普查提供了批量数据收集的一个很好的例子。人口普查会定期启动,并通过让人们挨家挨户走动,用这种蛮力的方式对美国公民进行发现和统计。这在 1790 年首次开始人口普查时很有意义。当时的数据收集本质上是面向批处理的,它涉及骑马四处走动并在纸上写下记录,然后将这批记录传输到汇总所有人口数据的中心位置。这些天来,当你描述人口普查过程时,你会立即想知道为什么我们不记录出生和死亡的日志,可以连续或以任何所需的粒度生成人口计数。

这是一个极端的例子,但许多数据传输过程仍然依赖于定期转储以及批量传输和集成。处理批量转储的唯一自然方法是使用批处理。但随着这些过程被连续推送所取代,人们自然会开始转向连续处理,以理清所需的处理资源并减少延迟。

例如,LinkedIn 几乎没有批量数据收集。我们的大部分数据要么是活动数据,要么是数据库更改,这两者都是连续发生的。事实上,当你考虑任何业务时,基本运行机制几乎总是一个连续的过程——事件是实时发生的,正如 Jack Bauer 告诉我们的那样。当数据批量收集时,几乎总是由于一些手动步骤或缺乏数字化,或者是一些非数字化过程变为自动化后遗留的历史问题。当是邮件通信或人类进行处理时,传输和对数据做出反应会非常缓慢。自动化的第一层处理始终保留原始流程的形式,因此这通常会持续很长时间。

每天运行的生产级别的“批处理”作业,通常是模仿窗口大小为一天的连续计算。当然,基础数据总是在变化。这些实际上在 LinkedIn 很常见(使它们在 Hadoop 中工作非常棘手),以至于我们实现了一个完整的框架来管理增量 Hadoop 工作流。

从这个角度来看,很容易对流处理有不同的看法:它只是在处理的基础数据中包含时间概念的处理,不需要数据的静态快照,因此它可以在用户可控的维度里调整生产速率,而不是等待到达数据集的“结束”。从这个意义上说,流处理是批处理的泛化,并且鉴于实时数据的普遍性,这是一个非常重要的泛化。

那么为什么流处理的传统观点一直是一个小众应用呢?我认为最大的原因是缺乏实时数据收集,使得连续处理成为学术问题。

我认为缺乏实​​时数据收集,可能是商业流处理系统注定要失败的原因。他们的客户仍在为 ETL 和数据集成进行面向文件的日常批处理。构建流处理系统的公司,专注于提供附加到实时数据流的处理引擎,但事实证明,当时很少有人真正拥有实时数据流。实际上,在我在 LinkedIn 的职业生涯早期,一家公司试图向我们推销一个非常酷的流处理系统,但由于当时我们所有的数据都是以每小时文件的形式收集的,所以我们能想出的最好的应用程序是每小时文件在小时结束时进入流系统!他们指出,这是一个相当普遍的问题。一个例外实际上证明了这里的规则:金融——流处理在这个领域取得了一些成功;在这个领域,数据流普遍是实时的,并且处理能力成为了瓶颈。

即使存在一个健康的批处理生态系统,我认为流处理作为一种基础架构,其实际适用性是相当广泛的。我认为它涵盖了实时请求/响应服务和离线批处理之间的基础设施差距。对于现代互联网公司,我认为他们大约 25% 的代码属于这一类。

事实证明,日志解决了流处理中的一些最关键的技术问题,我将对此进行描述,但它解决的最大问题只是让数据在实时多订阅者中可以进行推送。对于那些对更多细节感兴趣的人,我们开源了Samza,这是一个明确地建立在许多这些想法之上的流处理系统。我们在此处的文档中,更详细地描述了许多这些应用程序。

数据流图

流处理最有趣的方面与流处理系统的内部无关,而是与它如何扩展我们从早期数据集成讨论中对数据推送的概念有关。我们主要讨论了推送或日志的原始数据——在执行各种应用程序时产生的事件和数据。但是流处理允许我们还包括其他推送再次产生的推送。这些派生的推送对消费者而言,与计算它们的原始数据推送没有什么不同。这些派生的推送可以封装任意复杂性。

让我们深入研究一下。就我们的目的而言,流处理作业,从日志中读取,并将输出写入日志或其他系统。他们用于输入和输出的日志,组成了处理流程图不同阶段的各个步骤。实际上,以这种方式使用集中式日志,你可以将组织的所有数据的获取、转换和流动,视为只是写入它们的一系列日志和流程。

流处理器根本不需要花哨的框架:它可以是从日志读取和写入的任何进程或一组进程,但可以提供额外的基础设施和支持来帮助管理处理代码。

集成日志的目的有两个。

首先,它使每个数据集都是多订阅者和有序的。回想一下我们的“状态复制”原则,记住顺序的重要性。为了使这一点更具体,考虑一个来自数据库的更新流——如果我们在处理过程中,对同一记录重新排序两次更新操作,我们可能会产生错误的最终输出。这种顺序比 TCP 等提供的顺序更持久,因为它不限于单个点对点链接,并且在进程失败和重新连接之后仍然存在。

其次,日志为进程提供缓冲。这是非常根本的。如果处理以非同步方式进行,则上游作业产生数据的速度可能比另一个下游作业消耗数据的速度更快。发生这种情况时,处理必须阻塞、缓冲或丢弃数据。删除数据可能不是一种选择;阻塞可能会导致整个处理流程停止运行。日志充当一个非常非常大的缓冲区,允许进程重新启动或失败,而不会减缓处理流程的其他部分。当将此数据流扩展到更大的组织时,这种隔离尤其重要,在该组织中,处理任务由许多不同团队共同完成。我们不能有一个错误的工作导致停止整个处理流程的背压。

StormSamza都是以这种方式构建的,并且可以使用 Kafka 或其他类似系统作为它们的日志。

有状态的实时处理

一些实时流处理只是无状态的一次记录转换,但许多是用于更复杂的计数、聚合或窗口的连接。例如,一个人可能希望使用有关用户点击的信息来丰富事件流(例如点击流)——实际上是将点击流加入到用户帐户数据库中。这种处理最终需要处理器维护某种状态:例如,在计算计数时,你需要维护到目前为止的计数。如果处理器本身可能发生故障,如何正确维护这种状态?

最简单的选择是将状态保存在内存中。但是,如果进程崩溃,它将失去其中间状态。如果仅在一个窗口上维护状态,则该过程可能会回退到日志中窗口开始的位置。但是,如果一个人在每个小时进行技术,这大概是不可行的。

另一种方法是简单地将所有状态存储在远程存储系统中,并通过网络加入该存储。这样做的问题是没有数据的局部性和大量的网络往返。

我们如何支持像“表”这样与我们的处理分开的东西?

回想一下关于表和日志的二元性的讨论。这为我们提供了能够将流转换为与我们的处理位于同层级的表工具,以及处理这些表的容错机制。

流处理器可以将其状态保存在本地“表”或“索引”中——bdbleveldb,甚至更不寻常的东西,例如Lucenefastbit索引。这个存储的内容是从它的输入流中提供的(可能在第一次应用任意转换之后)。它可以为保留的本地索引记录一个更改日志,允许它在发生崩溃和重新启动时恢复其状态。这提供了一种通用机制,用于将任意索引类型中的共同分区状态与传入的流数据保持在本地。

当进程失败时,它会从更改日志中恢复其索引。日志是将本地状态转换为一次备份的一种增量记录。

这种状态管理方法非常优雅,使得处理器的状态也作为日志进行维护。我们可以认为这个日志就像我们对数据库表的更改日志一样。事实上,处理器有一些非常非常相似的共同分区表。由于这个状态本身就是一个日志,其他处理器也可以订阅它。处理的目标是更新最终状态,并且该状态是处理的自然输出——在这种情况下,非常有用。

当为了数据集成,将从数据库流出的日志结合使用时,日志/表二元性的力量就变得清晰起来。可以从数据库中提取更改日志,并由各种流处理器以不同的形式建立索引,最终集成到事件流中。

我们在 Samza 中详细介绍了这种管理状态处理的方式,并在此处提供了更多实际示例。

日志压缩


当然,我们不能希望始终为所有状态更改保留完整的日志。除非想使用无限空间,否则必须以某种方式清理日志。我将稍微谈谈在 Kafka 中的实现,以使其更加具体。在 Kafka 中,清理有两个选项,具体取决于数据是否包含键值更新或事件数据。对于事件数据,Kafka 支持只保留一个数据窗口。通常,这被配置为几天,但可以根据时间或空间来定义窗口。但是,对于键值数据,完整日志的一个很好的特性是,你可以通过重放它来重建源系统的状态(可能在另一个系统中重新创建它)。

但是,保留完整的日志会随着时间的推移使用越来越多的空间,并且回放的时间会越来越长。因此,在 Kafka 中,我们支持不同类型的保留。我们不是简单地丢弃旧日志,而是删除过时的记录——即主键有更新的记录。通过这样做,我们仍然保证日志包含源系统的完整备份,但现在我们不能再重新创建源系统的所有先前状态,只能重新创建最近的状态。我们将此功能称为日志压缩

用户头像

Rae

关注

人生,只要能照亮某个角落就够了。 2020.08.14 加入

互联网从业7年,后端老油条,背锅侠,前字节员工

评论

发布
暂无评论
【译】日志:每个软件工程师都应该了解实时数据的统一抽象【三】_kafka_Rae_InfoQ写作社区