本文被誉为程序员必读文章,学习消息队列的同学都应该看看。原文: The Log: What every software engineer should know about real-time data's unifying abstraction | LinkedIn Engineering
日志:每个软件工程师都应该了解实时数据的统一抽象
大约六年前,我在一个特别有趣的时候加入了 LinkedIn。我们刚刚开始遇到我们的单一集中式数据库的限制,需要开始过渡到专门的分布式系统组合。这是一次有趣的经历:我们构建、部署和运行了一个分布式图形数据库、一个分布式搜索后端、一个 Hadoop 安装以及第一代和第二代键值存储。
在这一切中我学到的最有用的东西之一是,我们正在构建的许多东西的核心都有一个非常简单的概念:日志。有时称为预写日志或提交日志或事务日志,日志几乎与计算机一样长,并且是许多分布式数据系统和实时应用程序架构的核心。
不了解日志就无法完全了解数据库、NoSQL 存储、键值存储、复制、paxos、hadoop、版本控制或几乎任何软件系统;然而,大多数软件工程师并不熟悉它们。我想改变它。在这篇文章中,我将带您了解有关日志的所有知识,包括什么是日志以及如何使用日志进行数据集成、实时处理和系统构建。
第一部分:什么是日志?
日志可能是最简单的存储抽象。它是按时间排序的仅追加、完全排序的记录序列。它看起来像这样:
记录附加到日志的末尾,读取从左到右进行。每个条目都分配有唯一的顺序日志条目编号。
记录的顺序定义了“时间”的概念,因为左侧的条目被定义为比右侧的条目更旧。日志条目号可以被认为是条目的“时间戳”。将这种排序描述为时间的概念起初似乎有点奇怪,但它具有方便的特性,即它与任何特定的物理时钟解耦。当我们进入分布式系统时,这个属性将变得至关重要。
就本次讨论而言,记录的内容和格式并不重要。此外,我们不能只向日志中添加记录,因为我们最终会用完空间。稍后我会回到这个问题。
因此,日志与文件或表并没有什么不同。文件是字节数组,表是记录数组,而日志实际上只是一种表或文件,其中记录按时间排序。
在这一点上,您可能想知道为什么值得谈论如此简单的事情?仅追加的记录序列与数据系统有什么关系?答案是日志有一个特定的目的:它们记录发生的事情和时间。对于分布式数据系统,这在很多方面都是问题的核心。
但在我们走得太远之前,让我澄清一些有点令人困惑的事情。每个程序员都熟悉日志的另一种定义——应用程序可能使用 syslog 或 log4j 写入本地文件的非结构化错误消息或跟踪信息。为清楚起见,我将其称为“应用程序日志记录”。应用程序日志是我所描述的日志概念的退化形式。最大的区别是文本日志主要是供人类阅读的,而我所描述的“日志”或“数据日志”是为编程访问而构建的。
(实际上,如果你仔细想想,人类阅读单个机器上的日志的想法有点不合时宜。当涉及到许多服务和服务器时,这种方法很快就会成为一种难以管理的策略,并且日志的目的很快就会成为查询和图表来理解许多机器上的行为——文件中的英文文本并不像这里描述的那种结构化日志那样合适。)
登录数据库
我不知道日志的概念起源于哪里——可能它是像二进制搜索这样的东西之一,它对于发明者来说太简单了,以至于无法意识到它是一项发明。它早在 IBM 的System R就出现了。数据库中的使用与在出现崩溃时保持各种数据结构和索引的同步有关。为了使这种原子性和持久性,数据库使用日志写出有关他们将修改的记录的信息,然后再将更改应用于它维护的所有各种数据结构。日志是所发生的事情的记录,每个表或索引都是将这段历史投影到一些有用的数据结构或索引中。由于日志会立即持久化,因此它被用作在发生崩溃时恢复所有其他持久性结构的权威来源。
随着时间的推移,日志的使用从 ACID 的实现细节发展为在数据库之间复制数据的方法。事实证明,数据库上发生的一系列更改正是保持远程副本数据库同步所需要的。Oracle、MySQL 和 PostgreSQL 包含日志传送协议,用于将部分日志传输到充当从属的副本数据库。Oracle 已将日志产品化为非 Oracle 数据订阅者的通用数据订阅机制,其XStreams和GoldenGate以及 MySQL 和 PostgreSQL 中的类似设施是许多数据架构的关键组件。
由于这个起源,机器可读日志的概念在很大程度上仅限于数据库内部。使用日志作为数据订阅机制似乎几乎是偶然出现的。但这种抽象非常适合支持各种消息传递、数据流和实时数据处理。
分布式系统中的日志
日志解决的两个问题——更改排序和分发数据——在分布式数据系统中更为重要。同意更新订单(或同意不同意并应对副作用)是这些系统的核心设计问题之一。
分布式系统的以日志为中心的方法源于一个简单的观察,我将其称为状态机复制原则:
这可能看起来有点迟钝,所以让我们深入了解它的含义。
确定性意味着处理不依赖于时间,并且不会让任何其他“带外”输入影响其结果。例如,一个程序的输出受线程的特定执行顺序或调用gettimeofday或一些其他不可重复的事物的影响,通常最好将其视为非确定性的。
进程的状态是处理结束时机器上保留的任何数据,无论是在内存中还是在磁盘上。
以相同的顺序获得相同的输入应该敲响警钟——这就是日志的来源。这是一个非常直观的概念:如果你将两个确定性的代码段提供给相同的输入日志,它们将产生相同的输出.
分布式计算的应用非常明显。您可以将让多台机器都做同样事情的问题减少到实现分布式一致日志以提供这些进程输入的问题。此处日志的目的是从输入流中挤出所有不确定性,以确保处理此输入的每个副本保持同步。
当您理解它时,这个原理并没有什么复杂或深刻的:它或多或少等于说“确定性处理是确定性的”。尽管如此,我认为它是分布式系统设计更通用的工具之一。
这种方法的优点之一是索引日志的时间戳现在充当副本状态的时钟——您可以用一个数字来描述每个副本,即它已处理的最大日志条目的时间戳。这个时间戳与日志相结合,唯一地捕获了副本的整个状态。
根据日志中的内容,有多种方法可以在系统中应用此原则。例如,我们可以记录对服务的传入请求,或者服务响应请求所经历的状态变化,或者它执行的转换命令。从理论上讲,我们甚至可以为每个副本记录一系列机器指令以执行,或者在每个副本上调用方法名称和参数。只要两个进程以相同的方式处理这些输入,这些进程将在副本之间保持一致。
不同的人群似乎对日志的用途有不同的描述。数据库人员通常会区分物理日志和逻辑日志。物理日志记录意味着记录更改的每一行的内容。逻辑日志记录不是记录更改的行,而是记录导致行更改的 SQL 命令(插入、更新和删除语句)。
分布式系统文献通常区分处理和复制的两种广泛方法。“状态机模型”通常是指一个主动-主动模型,我们记录传入请求的日志,每个副本处理每个请求。对此稍作修改,称为“主备份模型”,即选举一个副本作为领导者,并允许该领导者按照请求到达的顺序处理请求,并在处理请求时注销对其状态的更改。其他副本按照领导者所做的状态更改的顺序应用,以便它们同步并准备好在领导者失败时接管领导者。

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

分布式日志可以看作是对共识问题建模的数据结构。毕竟,日志代表了一系列关于要附加的“下一个”值的决定。你必须眯着眼睛才能看到 Paxos 算法家族中的日志,尽管日志构建是它们最常见的实际应用。对于 Paxos,这通常是使用称为“multi-paxos”的协议的扩展来完成的,它将日志建模为一系列共识问题,日志中的每个槽都有一个。日志在ZAB 、RAFT和Viewstamped Replication等其他协议中更为突出,这些协议直接对维护分布式、一致日志的问题进行建模。
我的怀疑是,我们对此的看法有点偏向于历史的轨迹,这可能是由于分布式计算理论在过去的几十年里超过了它的实际应用。实际上,共识问题有点太简单了。计算机系统很少需要决定单个值,它们几乎总是处理一系列请求。所以日志,而不是简单的单值寄存器,是更自然的抽象。
此外,对算法的关注掩盖了底层日志抽象系统的需求。我怀疑我们最终将更多地关注日志作为商品化的构建块,而不管它的实现方式,就像我们经常谈论哈希表而不费心去了解我们是指线性探测的杂音哈希还是一些细节其他变体。日志将成为某种商品化接口,许多算法和实现都在竞争以提供最佳保证和最佳性能。
变更日志 101:表和事件是双重的
让我们回到数据库。更改日志和表格之间存在着令人着迷的二元性。该日志类似于所有贷方和借方以及银行流程的列表;一张表是所有经常账户余额。如果您有更改日志,则可以应用这些更改以创建捕获当前状态的表。该表将记录每个键的最新状态(截至特定日志时间)。从某种意义上说,日志是更基础的数据结构:除了创建原始表之外,您还可以对其进行转换以创建各种派生表。(是的,表可能意味着非关系人员的键控数据存储。)

这个过程也反向进行:如果您有一个表进行更新,您可以记录这些更改并将所有更新的“更改日志”发布到表的状态。此更改日志正是您支持近实时副本所需要的。因此,从这个意义上说,您可以将表和事件视为双重的:表支持静态数据,而日志则捕获变化。日志的神奇之处在于,如果它是完整的更改日志,它不仅包含表的最终版本的内容,还允许重新创建可能已经存在的所有其他版本。实际上,它是表的每个先前状态的一种备份。
这可能会让您想起源代码版本控制。源代码控制和数据库之间有着密切的关系。版本控制解决了一个与分布式数据系统必须解决的问题非常相似的问题——管理分布式的并发状态更改。版本控制系统通常对补丁序列进行建模,这实际上是一个日志。您可以直接与当前代码的已签出“快照”进行交互,该“快照”类似于表格。您会注意到,在版本控制系统中,与在其他分布式有状态系统中一样,复制是通过日志进行的:当您更新时,您只需下载补丁并将它们应用到您当前的快照中。
最近,有些人从销售以日志为中心的数据库的公司Datomic看到了其中的一些想法。本演示文稿很好地概述了他们如何在他们的系统中应用这个想法。当然,这些想法并不是这个系统所独有的,因为十多年来它们一直是分布式系统和数据库文献的一部分。
这似乎有点理论化。不要灰心!我们很快就会得到实用的东西。
下一步是什么
在本文的其余部分中,我将尝试说明日志的好处超出了分布式计算或抽象分布式计算模型的内部结构。这包括:
- 数据集成——使组织的所有数据在其所有存储和处理系统中都易于使用。
- 实时数据处理——计算派生数据流。
- 分布式系统设计——如何通过以日志为中心的设计来简化实用系统。
这些使用都解决了将日志作为独立服务的想法。
在每种情况下,日志的用处都来自于日志提供的简单功能:生成持久的、可重复播放的历史记录。令人惊讶的是,这些问题的核心是能够以确定的方式以自己的速率让许多机器播放历史记录。
第二部分:数据集成
让我先说一下我所说的“数据集成”是什么意思以及为什么我认为它很重要,然后我们将看看它与日志的关系。
“数据集成”这个短语并不常见,但我不知道更好的短语。更容易识别的术语ETL通常只涵盖数据集成的有限部分——填充关系数据仓库。但我所描述的大部分内容可以被认为是 ETL 泛化以涵盖实时系统和处理流程。

在围绕大数据概念的所有令人窒息的兴趣和炒作中,您并没有听到太多关于数据集成的信息,但尽管如此,我相信“使数据可用”这个平凡的问题是组织可以关注的更有价值的事情之一.
数据的有效使用遵循一种

本文探讨了日志在数据集成、实时处理和分布式系统设计中的核心作用。日志不仅用于同步数据库和缓存,还支持数据流的实时订阅,促进数据集成,提供低延迟的流处理能力,并简化了系统间的复制和一致性。通过将日志作为通用接口,可以构建可扩展的基础设施,支持多订阅者实时数据流,同时降低数据集成的复杂性。日志在事件驱动系统和流处理框架中扮演着重要角色,为构建分布式系统提供了坚实的基础。
最低0.47元/天 解锁文章
5万+

被折叠的 条评论
为什么被折叠?



