写点什么

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

作者:rae
  • 2022 年 9 月 01 日
    浙江
  • 本文字数:4893 字

    阅读完需:约 16 分钟

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

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


作者是 Jay Kreps,Confluent 公司的首席执行官兼联合创始人。Confluent 是一家将 Apache Kafka 商业化的领先的基础设施软件公司。Jay Kreps 于 2011 年在 LinkedIn 担任工程师时创建了 Apache Kafka。通过作者的经历可以知道,怪不得作者对日志有如此深入浅出的讲解。


这篇文章是全文的第一部分,对日志进行了简单的介绍,引出了数据库同步、分布式共识、版本控制等一系列的话题,从本质上都是日志的处理。对日志也可以用文末一句核心的话加以总结

the log provides: producing a persistent, re-playable record of history.


其实计算机领域里,很多东西深入到底层,从基本原理上理解都是有相通性的。网上介绍项目实战、工具和语言使用的文章有很多,但是从核心原理深入浅出展开的很少。多学习一些基础的知识,其实是可以举一反三到很多方面的。

封面使用了《数据密集型应用系统设计》里第三章首页的图,第三章也讲解了日志结构的存储。顺便提一下,这也是本公认的神作!安利一波。



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

发布时间:2013 年 12 月 16 日


大约六年前,我在一个特别有趣的时候加入了 LinkedIn。我们刚刚开始遇到我们的单一集中式数据库的限制,需要开始过渡到专门的分布式系统组合。这是一次有趣的经历:我们构建、部署和运行了一个分布式图形数据库、一个分布式搜索后端、一个 Hadoop 以及第一代和第二代键值存储。


在这一切中我学到的最有用的东西之一是,我们正在构建的许多东西的核心都有一个非常简单的概念:日志。有时称为预写日志或提交日志或事务日志,日志几乎与计算机一样长,并且是许多分布式数据系统和实时应用程序架构的核心。


不了解日志就无法完全了解数据库、NoSQL 存储、键值存储、复制、paxos、hadoop、版本控制或几乎任何软件系统;然而,大多数软件工程师并不熟悉它们。我想改变它。在这篇文章中,我将带你了解有关日志的所有知识,包括什么是日志以及如何使用日志进行数据集成、实时处理和系统构建。

第一部分:什么是日志?

日志可能是最简单的存储抽象。它是按时间排序的仅追加、完全排序的记录序列。它看起来像这样:



记录附加到日志的末尾,读取从左到右进行。每个条目都分配有唯一的顺序日志条目编号。


记录的顺序定义了“时间”的概念,因为左侧的条目被定义为比右侧的条目更旧。日志条目号可以被认为是条目的“时间戳”。将这种排序描述为时间的概念起初似乎有点奇怪,但它具有方便的特性,即它与任何特定的物理时钟解耦。当我们进入分布式系统时,这个属性将变得至关重要。


就本次讨论而言,记录的内容和格式并不重要。此外,我们不能只向日志中添加记录,因为我们最终会用完空间。稍后我会回到这个问题。


因此,日志与文件或表并没有什么不同。文件是字节数组,表是记录数组,而日志实际上只是一种表或文件,其中记录按时间排序。


在这一点上,您可能想知道为什么值得谈论如此简单的事情?仅追加的记录序列与数据系统有什么关系?答案是日志有一个特定的目的:它们记录发生的事情和时间。对于分布式数据系统,这在很多方面都是问题的核心。


但在我们讨论更远之前,让我澄清一些有点令人困惑的事情。每个程序员都熟悉日志的另一种定义——应用程序可能使用 syslog 或 log4j 写入本地文件的非结构化错误消息或跟踪信息。为清楚起见,我将其称为“应用程序日志记录”。应用程序日志是我所描述的日志概念的退化形式。最大的区别是文本日志主要是供人类阅读的,而我所描述的“日志”或“数据日志”是为程序访问而构建的。


(实际上,如果你仔细想想,人类阅读单个机器上的日志的想法有点不合时宜。当涉及许多服务和服务器时,这种方法很快就会成为一种难以管理的策略,并且日志的目的很快就会成为查询和图表来理解许多机器上的行为——文件中的英文文本并不像这里描述的那种结构化日志那样合适。)

数据库日志

我不知道日志的概念起源于哪里——可能它是像二分搜索这样的东西之一,它对发明者来说太简单了,以至于无法意识到它是一项发明。它早在 IBM 的System R就出现了。考虑到崩溃的场景,它在数据库中的使用是为了保持各种数据结构和索引的同步。为了使这种操作具有原子性和持久性,数据库使用日志来记录将要修改的记录的信息,然后再将更改应用于它维护的所有各种数据结构。日志是所发生的事情的记录,每个表或索引都是将这段历史投影到一些有用的数据结构或索引中。由于日志会立即持久化,因此它被用作在发生崩溃时恢复所有其他持久性结构的可信来源。


随着时间的推移,日志的使用从 ACID 的实现细节发展为在数据库之间复制数据的方法。事实证明,数据库上发生的一系列更改正是保持远程副本数据库同步所需要的。Oracle、MySQL 和 PostgreSQL 包含日志传送协议,用于将部分日志传输到从属的副本数据库。Oracle 已将日志产品化为非 Oracle 数据订阅者的通用数据订阅机制,其XStreamsGoldenGate以及 MySQL 和 PostgreSQL 中的类似设施是许多数据架构的关键组件。


由于这个起源,机器可读日志的概念在很大程度上仅限于数据库内部。使用日志作为数据订阅机制似乎是偶然出现的。但这种抽象非常适合支持各种消息传递、数据流和实时数据处理。


分布式系统中的日志

日志解决的两个问题——更改排序和分发数据——在分布式数据系统中更为重要。同意更新的顺序(或不同意并应对副作用)是这些系统的核心设计问题之一。


分布式系统的以日志为中心的方法源于一个简单的观察,我将其称为状态机复制原则:

如果两个相同的确定性过程以相同的状态开始,并以相同的顺序获得相同的输入,它们将产生相同的输出并以相同的状态结束。

这可能看起来有点迟钝,所以让我们深入了解它的含义。


确定性意味着处理不依赖于时间,并且不会让任何其他“带外”输入影响其结果。例如,一个程序的输出受线程的特定执行顺序或调用gettimeofday或其他一些不可重复的事物影响,通常最好将其视为非确定性的。


进程的状态是处理结束时机器上保留的任何数据,无论是在内存中还是在磁盘上。


以相同的顺序获取相同的输入听起来很熟悉——这就是日志的来源。这是一个非常直观的概念:如果你给两个确定性的代码段提供相同的输入日志,它们将产生相同的输出。


分布式计算的应用非常明显。你可以通过实现分布式一致日志以提供进程输入,来解决让多台机器都做同样事情的问题。此处日志的目的是从输入流中排除所有不确定性,以确保处理此输入的每个副本保持同步。


当你理解它时,这个原理并没有什么复杂或深刻的:它或多或少等于说“确定性处理是确定性的”。尽管如此,我认为它是分布式系统设计更通用的工具之一。


这种方法的优点之一是索引日志的时间戳现在充当副本状态的时钟——您可以用一个数字来描述每个副本,即它已处理的最大日志条目的时间戳。这个时间戳与日志相结合,唯一地获取了副本的整个状态。


根据日志中的内容,有多种方法可以在系统中应用此原则。例如,我们可以记录对服务的传入请求,或者服务响应请求所经历的状态变化,或者它执行的转换命令。理论上,我们甚至可以为每个副本记录一系列机器指令以执行,或者在每个副本上调用方法和参数。只要两个进程以相同的方式处理这些输入,这些进程将在副本之间保持一致。


不同的人群似乎对日志的用途有不同的描述。数据库人员通常会区分物理日志和逻辑日志。物理日志记录意味着记录更改的每一行的内容。逻辑日志记录的不是记录更改的行,而是记录导致行更改的 SQL 命令(插入、更新和删除语句)。


分布式系统文献通常区分处理和复制这两种常用方法。“状态机模型”通常是指一个主动-主动模型,我们记录传入请求的日志,每个副本处理每个请求。对此稍作修改,称为“主备模型”,是选举一个副本作为领导者,并允许该领导者按照请求到达的顺序处理请求,并在处理请求时记录其状态的更改。其他副本按照领导者所做的状态更改进行顺序应用,以便它们同步,并准备好在领导者失败时接管领导者。



要了解这两种方法之间的区别,让我们看一个玩具问题。考虑一个复制的“算术服务”,它维护一个数字作为其状态(初始化为零)并对这个值执行加法和乘法。主动-主动方法可能会记录要执行的转换,例如“+1”、“*2”等。每个副本都会执行这些转换,因此会经历相同的一组值。“主动-被动”方法将让单个主服务器执行转换并注销结果,例如“1”、“3”、“6”等。这个例子也清楚地说明了为什么排序是确保副本一致性的关键:重新排序加法和乘法将产生不同的结果。



分布式日志可以看作是对共识问题建模的数据结构。毕竟,日志代表了一系列要添加“下一个”值的决定。你必须眯着眼睛才能看到 Paxos 算法家族中的日志,尽管日志构建是它们最常见的实际应用。对于 Paxos,这通常是使用称为“multi-paxos”的协议的扩展来完成的,它将日志建模为一系列共识问题,日志中的每个槽都有一个。日志在ZAB 、RAFTViewstamped Replication等其他协议中更为突出,这些协议直接对维护分布式、一致日志的问题进行建模。


我的怀疑是,我们对此的看法有点偏向于历史的轨迹,这可能是由于分布式计算理论在过去的几十年里超过了它的实际应用。实际上,共识问题有点太简单了。计算机系统很少需要决定单个值,它们几乎总是处理一系列请求。所以日志,而不是简单的单值寄存器,是更自然的抽象。


此外,对算法的关注掩盖了底层日志抽象系统的需求。我怀疑我们最终会更多地关注日志作为通用化的构建块,而不管它的实现方式,就像我们经常谈论哈希表,而不费心去了解我们是指线性探测的 Murmur 哈希还是其他变体。日志将成为某种通用化接口,会有许多算法和实现进行竞争以提供最佳保障和最优性能。


变更日志 101:表和事件是双重的

让我们回到数据库。更改日志和表格之间存在着令人着迷的二元性。日志类似于所有借贷以及银行流水的列表;一张表是所有账户余额。如果你有更改日志,则可以应用这些更改以获取当前状态的表。该表将记录每个键的最新状态(截至特定日志时间)。从某种意义上说,日志是更基础的数据结构:除了创建原始表之外,你还可以对其进行转换以创建各种派生表。(是的,表可能意味着非关系数据的关键存储。)


这个过程也反向进行:如果你有一个表在更新,您可以记录这些更改,并发布所有表状态的更新的“更改日志”。此更改日志正是你实现近实时副本所需要的。因此,从这个意义上说,您可以将表和事件视为双重的:表支持静态数据,而日志则捕获变化。日志的神奇之处在于,如果它是完整的更改日志,它不仅包含表的最终版本的内容,还允许重新创建可能已经存在的所有其他版本。实际上,它是表的每个先前状态的一种备份。


这可能会让您想起源代码版本控制。源代码控制和数据库之间有着密切的关系。版本控制解决了一个与分布式数据系统必须解决的问题非常相似的问题——管理分布式的并发状态更改。版本控制系统通常对补丁序列进行建模,这实际上是一个日志。您可以直接与当前代码的已检出“快照”进行交互,该“快照”类似于表格。您会注意到,在版本控制系统中,与在其他分布式有状态系统中一样,复制是通过日志进行的:当您更新时,您只需下载补丁并将它们应用到您当前的快照中。


最近,有些人从销售以日志为中心的数据库的公司Datomic看到了其中的一些想法。本演示文稿很好地概述了他们如何在他们的系统中应用这个想法。当然,这些想法并不是这个系统所独有的,因为十多年来它们一直是分布式系统和数据库文献的一部分。


这似乎有点理论化。不要绝望!我们很快就会进入实用的东西。


下一步是什么

在本文的其余部分中,我将尝试说明日志的好处超出了分布式计算核心或抽象分布式计算模型。这包括:

  1. 数据集成——使组织的所有数据在其所有存储和处理系统中都易于使用。

  2. 实时数据处理——计算派生的数据流。

  3. 分布式系统设计——如何通过以日志为中心的设计来简化实用系统。


这些使用都解决了将日志作为独立服务的想法。

在每种情况下,日志的用处都来自于日志提供的简单功能:生成持久的、可重复播放的历史记录。令人惊讶的是,这些问题的核心是,能够以确定的方式按照自己的速率让许多机器回放历史。

用户头像

rae

关注

还未添加个人签名 2020.08.14 加入

还未添加个人简介

评论

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