The Log:每个程序员都应该知道有关实时数据的统一抽象(1)概念

// 本文基于开源中国社区的译文稿: 《日志:每个软件工程师都应该知道的有关实时数据的统一概念

译序

这篇文章是 LinkedIn 的 Kreps 发表的一篇博文,虽然很长,但被称为程序员史诗般必读文章@foreach_break  的《学习笔记:The Log(我所读过的最好的一篇分布式技术文章)》对本文做了很赞的摘要和解读。

但作为一篇经典文章,还是值得去完整地研读和理解:

  1. 原文可以作为大数据/分布式系统领域一份导论式的资料。 作者对整个领域的理解和实战精深广博,抓出并梳理了这个领域的核心:日志。
  2. 原文作为一手资料,有完整的分析过程,能够深入和核对自己的理解。
  3. 摘要和解读不能替代自己理解。 信息被传递和过滤得越多,丢失和偏差也就越多。

当然,你也可以把这篇译文本身作为英文原文的一种理解,在读原文时有不理解的地方可以参考对比。 如果你能这么做,相信对于学习效果真真是极好的~

自己理解粗浅且这篇文章又长难度又大,译文源码在GitHub,翻译中肯定会有不少不足和不对之处, 欢迎建议(提交Issue)和指正(Fork后提交代码)!

日志:每个软件工程师都应该知道的有关实时数据的统一抽象

我在六年前加入到LinkedIn公司,那是一个令人兴奋的时刻:我们刚开始面临单一庞大的集中式数据库的限制问题,需要过渡到一套专门的分布式系统。 这是一个令人兴奋的经历:我们构建、部署和运行分布式图数据库(distributed graph database)、分布式搜索后端(distributed search backend)、 Hadoop以及第一代和第二代键值数据存储(key-value store),而且这套系统一直运行至今。

这个过程中,我学到的最有益的事情是我们所构建这套系统的许多组件其核心都包含了一个很简单的概念:日志。 日志有时会叫成 预先写入日志(write-ahead logs)、提交日志(commit logs)或者事务日志(transaction logs),几乎和计算机本身形影不离, 是许多分布式数据系统(distributed data system)和实时应用架构(real-time application architecture)的核心。

不懂得日志,你就不可能真正理解数据库、NoSQL存储、键值存储(key value store)、数据复制(replication)、paxosHadoop、版本控制(version control),甚至几乎任何一个软件系统;然而大多数软件工程师对日志并不熟悉。我有意于改变这个现状。 本文我将带你浏览有关日志需要了解的一切,包括日志是什么,如何在数据集成(data integration)、实时处理(real time processing)和系统构建中使用日志。

第一部分:日志是什么?

日志可能是一种最简单的不能再简单的存储抽象,只能追加、按照时间完全有序(totally-ordered)的记录序列。日志看起来的样子:

log

在日志的末尾添加记录,读取日志记录则从左到右。每一条记录都指定了一个唯一的顺序的日志记录编号。

日志记录的次序(ordering)定义了『时间』概念,因为位于左边的日志记录表示比右边的要早。 日志记录编号可以看作是这条日志记录的『时间戳』。 把次序直接看成是时间概念,刚开始你会觉得有点怪异,但是这样的做法有个便利的性质:解耦了 时间 和 任一特定的物理时钟(physical clock)。 引入分布式系统后,这会成为一个必不可少的性质。

【译注】 分布式系统的 时间、次序、时钟是个最基础根本的问题,详见被引用最多的Leslie Lamport的论文Time Clocks and the Ordering of Events in a Distributed System中文翻译),现在先 不要 去看,除非读完本文后你还是有很兴趣要探个明白!

日志记录的内容和格式是什么对于本文讨论并不重要。另外,不可能一直给日志添加记录,因为总会耗尽存储空间。稍后我们会再回来讨论这个问题。

所以,日志 和 文件或数据表(table)并没有什么大的不同。文件是一系列字节,表是由一系列记录组成,而日志实际上只是一种按照时间顺序存储记录的数据表或文件。

讨论到现在,你可能奇怪为什么要讨论这么简单的概念?只能追加的有序的日志记录究竟又是怎样与数据系统生产关系的? 答案是日志有其特定的目标:它记录了什么时间发生了什么事情。而对分布式数据系统,在许多方面,这是要解决的问题的真正核心。

不过,在我们进行更加深入的讨论之前,让我先澄清有些让人混淆的概念。每个程序员都熟悉另一种日志记录的定义 —— 应用使用syslog或者log4j写入到本地文件里的无结构的错误信息或者追踪信息。为了区分,这种情形的称为『应用日志记录』。 应用日志记录是我说的日志概念的一种退化。两者最大的区别是:文本日志意味着主要用来方便人去阅读,而构建我所说的『日志(journal)』或者『数据日志(data logs)』是用于程序的访问。

(实际上,如果你深入地思考一下,会觉得人去阅读某个机器上的日志这样的想法有些落伍过时了。 当涉及很多服务和服务器时,这样的做法很快就变得难于管理, 我们的目的很快就变成 输入查询 和 输出用于理解多台机器的行为的图表, 因此,文件中的字句文本 几乎肯定不如 本文所描述的结构化日志 更合适。)

数据库中的日志

我不知道日志概念的起源 —— 可能就像二分查找(binary search)一样,发明者觉得太简单了而不是一项发明。早在IBM系统R出现时候日志就出现了。 在数据库里的用法是在崩溃的时候用它来保持各种数据结构和索引的同步。为了保证操作的原子性(atomic)和持久性(durable), 在对数据库维护的所有各种数据结构做更改之前,数据库会把要做的更改操作的信息写入日志。 日志记录了发生了什么,而每个表或者索引都是更改历史中的一个投影。由于日志是立即持久化的,发生崩溃时,可以作为恢复其他所有持久化结构的可靠来源。

随着时间的推移,日志的用途从ACID的实现细节成长为数据库间复制数据的一种方法。 结果证明,发生在数据库上的更改序列 即是 与远程副本数据库(replica database)保持同步 所需的操作。 OracleMySQLPostgreSQL都包括一个日志传送协议(log shipping protocol),传输日志给作为备库(Slave)的复本(replica)数据库。 Oracle还把日志产品化为一个通用的数据订阅机制,为非Oracle数据订阅用户提供了XStreamsGoldenGate,在MySQLPostgreSQL中类似设施是许多数据架构的关键组件。

正是由于这样的起源,机器可识别的日志的概念主要都被局限在数据库的内部。日志作为做数据订阅机制的用法似乎是偶然出现的。 但这正是支持各种的消息传输、数据流和实时数据处理的理想抽象。

分布式系统中的日志

日志解决了两个问题:更改动作的排序和数据的分发,这两个问题在分布式数据系统中更是尤为重要。 协商达成一致的更改动作的顺序(或是协商达成不一致做法并去做有副作用的数据拷贝)是分布式系统设计的核心问题之一。

分布式系统以日志为中心的方案是来自于一个简单的观察,我称之为状态机复制原理State Machine Replication Principle):

如果两个相同的、确定性的进程从同一状态开始,并且以相同的顺序获得相同的输入,那么这两个进程将会生成相同的输出,并且结束在相同的状态。

听起来有点难以晦涩,让我们更加深入的探讨,弄懂它的真正含义。

确定性deterministic)意味着处理过程是与时间无关的,而且不让任何其他『带外数据(out of band)』的输入影响处理结果。 例如,如果一个程序的输出会受到线程执行的具体顺序影响,或者受到getTimeOfDay调用、或者其他一些非重复性事件的影响,那么这样的程序一般被认为是非确定性的。

进程状态 是进程保存在机器上的任何数据,在进程处理结束的时候,这些数据要么保存在内存里,要么保存在磁盘上。

当碰到以相同的顺序输入相同的内容的情况时,应该触发你的条件反射:这个地方要引入日志。 下面是个很直觉的意识:如果给两段确定性代码相同的日志输入,那么它们就会生产相同的输出。

应用到分布式计算中就相当明显了。你可以把用多台机器都执行相同事情的问题化简为实现用分布式一致性日志作为这些处理的输入的问题。 这里日志的目的是把所有非确定性的东西排除在输入流之外,以确保处理这些输入的各个复本(replica)保持同步。

当你理解了这个以后,状态机复制原理就不再复杂或深奥了:这个原理差不多就等于说的是『确定性的处理过程就是确定性的』。不管怎样,我认为它是分布式系统设计中一个更通用的工具。

这样方案的一个美妙之处就在于:用于索引日志的时间戳 就像 用于保持副本状态的时钟 —— 你可以只用一个数字来描述每一个副本,即这个副本已处理的最大日志记录的时间戳。 日志中的时间戳 一一对应了 副本的完整状态。

根据写进日志的内容,这个原理可以有不同的应用方式。举个例子,我们可以记录一个服务的输入请求日志,或者从请求到响应服务的状态变化日志,或者服务所执行的状态转换命令的日志。 理论上来说,我们甚至可以记录各个副本执行的机器指令序列的日志 或是 所调用的方法名和参数序列的日志。 只要两个进程用相同的方式处理这些输入,这些副本进程就会保持一致的状态。

对日志用法不同群体有不同的说法。数据库工作者通常说成物理日志(physical logging)和逻辑日志(logical logging)。物理日志是指记录每一行被改变的内容。逻辑日志记录的不是改变的行而是那些引起行的内容改变的SQL语句(insertupdatedelete语句)。

分布式系统文献通常把处理和复制(processing and replication)方案宽泛地分成两种。『状态机器模型』常常被称为主-主模型(active-active model), 记录输入请求的日志,各个复本处理每个请求。 对这个模型做了细微的调整称为『主备模型』(primary-backup model),即选出一个副本做为leader,让leader按请求到达的顺序处理请求,并输出它请求处理的状态变化日志。 其他的副本按照顺序应用leader的状态变化日志,保持和leader同步,并能够在leader失败的时候接替它成为leader

active_and_passive_arch

为了理解两种方式的差异,我们来看一个不太严谨的例子。假定有一个要复制的『算法服务』,维护一个独立的数字作为它的状态(初始值为0),可以对这个值进行加法和乘法运算。 主-主方式所做的可能的是输出所进行的变换的日志,比如『+1』、『*2』等。各个副本都会应用这些变换,从而经过一系列相同的值。 而主备方式会有一个独立的Master执行这些变换并输出结果日志,比如『1』、『3』、『6』等。 这个例子也清楚的展示了为什么说顺序是保证各副本间一致性的关键:加法和乘法的顺序的改变将会导致不同的结果。

paxos_postcard

分布式日志可以看作是建模一致性)(consensus)问题的数据结构。 因为日志代表了『下一个』追加值的一系列决策。 你需要眯起眼睛才能从Paxos)算法簇中找到日志的身影,尽管构建日志是它们最常见的实际应用。 Paxos通过称为multi-paxos的一个扩展协议来构建日志,把日志建模为一系列一致性值的问题,日志的每个记录对应一个一致性值。 日志的身影在ZABRAFTViewstamped Replication等其它的协议中明显得多,这些协议建模的问题直接就是维护分布式一致的日志。

个人有一点感觉,在这个问题上,我们的思路被历史发展有些带偏了,可能是由于过去的几十年中,分布式计算的理论远超过了其实际应用。 在现实中,一致性问题是有点被过于简单化了。计算机系统几乎不需要决定单个的值,要的是处理一序列的请求。 所以,日志而不是一个简单的单值寄存器,是更自然的抽象。

此外,对算法的专注掩盖了系统底层所需的日志抽象。 个人觉得,我们最终会更关注把日志作为一个商品化的基石而不是考虑它的实现,就像我们经常讨论哈希表而不纠结于它的细节, 比如使用线性探测的杂音哈希(the murmur hash with linear probing)还是某个变种。 日志将成为一种大众化的接口,可以有多种竞争的算法和实现,以提供最好的保证和最佳的性能。

变更日志101:表与事件的二象性(duality

让我们继续聊一下数据库。变更日志 和 表之间有着迷人的二象性。 日志类似借贷清单和银行处理流水,而数据库表则是当前账户的余额。如果有变更日志,你就可以应用这些变更生成数据表并得到当前状态。 表记录的是每条数据的最后状态(日志的一个特定时间点)。 可以认识到日志是更基本的数据结构:日志除了可用来创建原表,也可以用来创建各类衍生表。 (是的,表可以是非关系型用户用的键值数据存储(keyed data store)。)

yin-yang

这个过程也是可逆的:如果你对一张表进行更新,你可以记录这些变更,并把所有更新的『变更日志(changelog)』发布到表的状态信息中。 这些变更日志正是你所需要的支持准实时的复制。 基于此,你就可以清楚的理解表与事件的二象性: 表支持了静态数据,而日志记录了变更。日志的魅力就在于它是变更的完整记录,它不仅仅包含了表的最终版本的内容, 而且可以用于重建任何存在过其它版本。事实上,日志可以看作是表每个历史状态的一系列备份。

【译注】 关于『二象性(duality)』:

二象性(duality)是个冷门词汇。我唯一在『光的波粒二象性(wave-particle duality』一词中有接触。 这里是指光(电磁波)有时会显示出波动性(这时粒子性较不显著),有时又会显示出粒子性(这时波动性较不显著),在不同条件下分别表现出波动或粒子的性质。 波动性/粒子性是对人们对光的认识、理解或描述方式。类似的,数据表现出了表与事件的二象性,表与事件是数据不同条件/场景下数据的性质,是对人们对数据的认识、理解或描述方式。

如果上面描述你觉得『二象』一词还是很难理解,你可以理解成『对偶』、『互通』、『对称』或『可逆』,即数据表 和 数据事件之间可以互相转化。

多谢@foreach-break的解释和评注。

这可能会让你想到源代码的版本控制(source code version control)。源码控制和数据库之间有着密切的关系。 版本管理解决了一个和分布式数据系统要解决的很类似的问题 —— 管理分布式的并发的状态变更。 版本管理系统建模的是补丁序列(the sequence of patches),实际上这就是日志。 你可以检出当前的代码的一个『快照』并直接操作,这个代码快照可以类比成表。 你会注意到,正如有状态的分布式系统一样,版本控制系统通过日志来完成复制:更新代码即是拉下补丁并应用到你的当前快照中。

从销售日志数据库的公司Datomic那里,大家可以看到一些这样的想法。 这个视频比较好地介绍了他们如何在系统中应用这些想法。 当然这些想法不是只专属于这个系统,这十多年他们贡献了很多分布式系统和数据库方面的文献。

这节的内容可能有点理论化了。但别沮丧!后面马上就是实操的干货。

接下来的内容

本文剩下的内容,我会试着重点讲述,除了作为分布式计算内部实现或模型抽象,日志有什么优点。包含:

  1. 数据集成Data Integration) —— 让组织中所有存储和处理系统可以容易地访问组织所有的数据。
  2. 实时数据处理 —— 计算生成的数据流。
  3. 分布式系统设计 —— 如何通过集中式日志的设计来简化实际应用系统。

所有这些用法都是通过把日志用做单独服务来实现的。

上面这些场景中,日志的好处都来自日志所能提供的简单功能:生成持久化的可重放的历史记录。 令人意外的是,能让多台机器以确定性的方式(deterministic manner)按各自的速度重放历史记录的能力是这些问题的核心。


. 第二部分:数据集成 »

打赏支持我翻译更多好文章,谢谢!

打赏译者

打赏支持我翻译更多好文章,谢谢!

任选一种支付方式

1 6 收藏 10 评论

关于作者:李鼎

花名哲良 PAAS平台@阿里云/分布式服务&服务治理框架dubbo。 个人主页 · 我的文章 · 11 ·     

相关文章

可能感兴趣的话题



直接登录
最新评论
  • 下雨看世界 发电厂运行 2015/08/21

    这个可以引用你的网址信息吗?放在我的博客中?

  • foreach_break 互联网 2015/08/22

    感谢@。

    这篇文章没有足够的经验和理论,是很难完全读懂和领会的。

    赞一个认真的精神。

    小小建议:
    1、有的东西没有必要硬翻,有的东西可能已经是约定俗成的专用概念和专有名词,比如active-active翻译为主动-主动,内行可以理解,但感觉有点“硬”。主-主即可。
    2、表与事件的二相性(duality),这里二相性翻译有误,是不是译为对偶或互通更好点。毕竟jay这里是强调对称、可逆等关系,如果译为二相性,意义就不对啦。

    • 李鼎 软件工程师 2015/08/22

      多谢 @foreach_break 兄 指正!

      > 1、有的东西没有必要硬翻,有的东西可能已经是约定俗成的专用概念和专有名词,比如active-active翻译为主动-主动,内行可以理解,但感觉有点“硬”。主-主即可。
      收到!已更新。

    • 李鼎 软件工程师 2015/08/22

      > 表与事件的二相性(duality),这里二相性翻译有误,是不是译为对偶或互通更好点。毕竟jay这里是强调对称、可逆等关系,如果译为二相性,意义就不对啦。

      翻译过程中,先​​拜读过你的《学习笔记:The Log(我所读过的最好的一篇分布式技术文章)》,你用的是『对偶性』。

      翻译中还是使用『二相性』是因为:

      1. 『二相性』一词有出现在『光的波粒二相性』这样的说明中,以描述光在不同场景表现的不同规则、表现或是理解方式(波 vs. 粒子)。个人觉得这个词 比较契合了 『数据的表与事件的二相性』,类比『二相性』是描述 数据在不同场景表现的不同规则、表现或是理解方式(表 vs. 事件)。
      2. 同时,『二相性』与 英文原词duality 是一致的。

      所以,就保留这个译法了。

      @foreach_break 兄 觉得如何? :)

      • foreach_break 互联网 2015/08/22

        二相性其实没有太大问题。
        我的意思可能这个翻译增加了理解难度。

        1、考虑到原文的dual和duality的用法:
        "Let's come back to databases for a bit. There is a facinating duality between a log of changes and a table. The log is similar to the list of all credits and debits and bank processes; a table is all the current account balances. If you have a log of changes, you can apply these changes in order to create the table capturing the current state. This table will record the latest state for each key (as of a particular log time). There is a sense in which the log is the more fundamental data structure: in addition to creating the original table you can also transform it to create all kinds of derived tables. (And yes, table can mean keyed data store for the non-relational folks.)"
        我觉得jay在强调change-log和table的相互转化关系,就像其配图的太极,阴阳可以转化。

        2、其后,有增加了可逆的描述,作为对第一条解读的印证。

        3、波粒二象性中的象和“相”在物理上好像还不是一个东西,但我没有考证,虽然我学应用物理的,哈哈,学残了。波粒二象性是说:波,特别是电磁波,拥有两种状态:波动和粒子。
        -- 比如光(一种电磁波)的衍射表现出波动性。
        -- 比如光电效应,表现出粒子性。

        4、如果从光所具有的属性来看,其拥有二象性用duality来表示,完全正确。

        5、注意到jay在原文中使用了"Changelog 101: Tables and Events are Dual",我当时在理解时,是基于事件和表的相互关系来理解,再结合上述几条的考虑,将duality译为了对偶,更强调其对称、对应、可逆、可相互转化的关系。

        6、二相性中的相,比如水的相态(很久了,不确定大学时的记忆是否准确了),有液、汽、冰。

        所以,我觉得,如果译为二相,没有很大的问题,但比对偶和互通、互相转化等译法更难懂了。毕竟了解相态的人不多。

        • 李鼎 软件工程师 2015/08/22

          > 我学应用物理的
          我把刚才搜了一下,确实是用『波粒二象性』,非『相』字,哈哈,果然物理专业的!

          - 我把译文的词改成『二象性』,译文好比较直接对应原作者的用词用义。
          - 把你的解释说明,作为一个译注,方便大家的理解内涵。

          多谢,@foreach_break 兄 指点 !

          • foreach_break 互联网 2015/08/22

            Lee兄格物致知的严谨精神值得我们去学习 :]

            嗯。我那个是笔记,粗读,很多不一定尊重原著,所以大白话和自己的理解为主。

            如果作为翻译,Lee兄这样做是没有问题哒。

      • foreach_break 互联网 2015/08/22

        写了一大堆,好像也没个明确的概括,哈哈,补充一下:
        1、整篇文章的Log,最核心的作用是记录了何时发生了什么。
        2、change-log就是这种变化序列的记录。
        3、table就是状态记录。

        两种记录之间可以互相转化,我觉得是这个意思,所以用了互通和对偶。

跳到底部
返回顶部