The Log每个程序员都应该知道有关实时数据的统一抽象.docx
- 文档编号:15181295
- 上传时间:2023-07-02
- 格式:DOCX
- 页数:35
- 大小:588.23KB
The Log每个程序员都应该知道有关实时数据的统一抽象.docx
《The Log每个程序员都应该知道有关实时数据的统一抽象.docx》由会员分享,可在线阅读,更多相关《The Log每个程序员都应该知道有关实时数据的统一抽象.docx(35页珍藏版)》请在冰点文库上搜索。
TheLog每个程序员都应该知道有关实时数据的统一抽象
TheLog:
每个程序员都应该知道有关实时数据的统一抽象
(1)概念
本文基于开源中国社区的译文稿:
《日志:
每个软件工程师都应该知道的有关实时数据的统一概念》
译序
这篇文章是 LinkedIn 的 Kreps 发表的一篇博文,虽然很长,但被称为程序员史诗般必读文章。
@foreach_break 的《学习笔记:
TheLog(我所读过的最好的一篇分布式技术文章)》对本文做了很赞的摘要和解读。
但作为一篇经典文章,还是值得去完整地研读和理解:
1.原文可以作为大数据/分布式系统领域一份导论式的资料。
作者对整个领域的理解和实战精深广博,抓出并梳理了这个领域的核心:
日志。
2.原文作为一手资料,有完整的分析过程,能够深入和核对自己的理解。
3.摘要和解读不能替代自己理解。
信息被传递和过滤得越多,丢失和偏差也就越多。
当然,你也可以把这篇译文本身作为英文原文的一种理解,在读原文时有不理解的地方可以参考对比。
如果你能这么做,相信对于学习效果真真是极好的~
自己理解粗浅且这篇文章又长难度又大,译文源码在GitHub上,翻译中肯定会有不少不足和不对之处,欢迎建议(提交Issue)和指正(Fork后提交代码)!
日志:
每个软件工程师都应该知道的有关实时数据的统一抽象
我在六年前加入到LinkedIn公司,那是一个令人兴奋的时刻:
我们刚开始面临单一庞大的集中式数据库的限制问题,需要过渡到一套专门的分布式系统。
这是一个令人兴奋的经历:
我们构建、部署和运行分布式图数据库(distributedgraphdatabase)、分布式搜索后端(distributedsearchbackend)、 Hadoop以及第一代和第二代键值数据存储(key-valuestore),而且这套系统一直运行至今。
这个过程中,我学到的最有益的事情是我们所构建这套系统的许多组件其核心都包含了一个很简单的概念:
日志。
日志有时会叫成预先写入日志(write-aheadlogs)、提交日志(commitlogs)或者事务日志(transactionlogs),几乎和计算机本身形影不离,是许多分布式数据系统(distributeddatasystem)和实时应用架构(real-timeapplicationarchitecture)的核心。
不懂得日志,你就不可能真正理解数据库、NoSQL存储、键值存储(keyvaluestore)、数据复制(replication)、paxos、Hadoop、版本控制(versioncontrol),甚至几乎任何一个软件系统;然而大多数软件工程师对日志并不熟悉。
我有意于改变这个现状。
本文我将带你浏览有关日志需要了解的一切,包括日志是什么,如何在数据集成(dataintegration)、实时处理(realtimeprocessing)和系统构建中使用日志。
第一部分:
日志是什么?
日志可能是一种最简单的不能再简单的存储抽象,只能追加、按照时间完全有序(totally-ordered)的记录序列。
日志看起来的样子:
在日志的末尾添加记录,读取日志记录则从左到右。
每一条记录都指定了一个唯一的顺序的日志记录编号。
日志记录的次序(ordering)定义了『时间』概念,因为位于左边的日志记录表示比右边的要早。
日志记录编号可以看作是这条日志记录的『时间戳』。
把次序直接看成是时间概念,刚开始你会觉得有点怪异,但是这样的做法有个便利的性质:
解耦了时间和任一特定的物理时钟(physicalclock)。
引入分布式系统后,这会成为一个必不可少的性质。
【译注】 分布式系统的时间、次序、时钟是个最基础根本的问题,详见被引用最多的LeslieLamport的论文TimeClocksandtheOrderingofEventsinaDistributedSystem(中文翻译),现在先 不要 去看,除非读完本文后你还是有很兴趣要探个明白!
日志记录的内容和格式是什么对于本文讨论并不重要。
另外,不可能一直给日志添加记录,因为总会耗尽存储空间。
稍后我们会再回来讨论这个问题。
所以,日志和文件或数据表(table)并没有什么大的不同。
文件是一系列字节,表是由一系列记录组成,而日志实际上只是一种按照时间顺序存储记录的数据表或文件。
讨论到现在,你可能奇怪为什么要讨论这么简单的概念?
只能追加的有序的日志记录究竟又是怎样与数据系统生产关系的?
答案是日志有其特定的目标:
它记录了什么时间发生了什么事情。
而对分布式数据系统,在许多方面,这是要解决的问题的真正核心。
不过,在我们进行更加深入的讨论之前,让我先澄清有些让人混淆的概念。
每个程序员都熟悉另一种日志记录的定义——应用使用syslog或者log4j写入到本地文件里的无结构的错误信息或者追踪信息。
为了区分,这种情形的称为『应用日志记录』。
应用日志记录是我说的日志概念的一种退化。
两者最大的区别是:
文本日志意味着主要用来方便人去阅读,而构建我所说的『日志(journal)』或者『数据日志(datalogs)』是用于程序的访问。
(实际上,如果你深入地思考一下,会觉得人去阅读某个机器上的日志这样的想法有些落伍过时了。
当涉及很多服务和服务器时,这样的做法很快就变得难于管理,我们的目的很快就变成输入查询和输出用于理解多台机器的行为的图表,因此,文件中的字句文本几乎肯定不如本文所描述的结构化日志更合适。
)
数据库中的日志
我不知道日志概念的起源——可能就像二分查找(binarysearch)一样,发明者觉得太简单了而不是一项发明。
早在IBM的系统R出现时候日志就出现了。
在数据库里的用法是在崩溃的时候用它来保持各种数据结构和索引的同步。
为了保证操作的原子性(atomic)和持久性(durable),在对数据库维护的所有各种数据结构做更改之前,数据库会把要做的更改操作的信息写入日志。
日志记录了发生了什么,而每个表或者索引都是更改历史中的一个投影。
由于日志是立即持久化的,发生崩溃时,可以作为恢复其他所有持久化结构的可靠来源。
随着时间的推移,日志的用途从ACID的实现细节成长为数据库间复制数据的一种方法。
结果证明,发生在数据库上的更改序列即是与远程副本数据库(replicadatabase)保持同步所需的操作。
Oracle、MySQL 和PostgreSQL都包括一个日志传送协议(logshippingprotocol),传输日志给作为备库(Slave)的复本(replica)数据库。
Oracle还把日志产品化为一个通用的数据订阅机制,为非Oracle数据订阅用户提供了XStreams和GoldenGate,在MySQL和PostgreSQL中类似设施是许多数据架构的关键组件。
正是由于这样的起源,机器可识别的日志的概念主要都被局限在数据库的内部。
日志作为做数据订阅机制的用法似乎是偶然出现的。
但这正是支持各种的消息传输、数据流和实时数据处理的理想抽象。
分布式系统中的日志
日志解决了两个问题:
更改动作的排序和数据的分发,这两个问题在分布式数据系统中更是尤为重要。
协商达成一致的更改动作的顺序(或是协商达成不一致做法并去做有副作用的数据拷贝)是分布式系统设计的核心问题之一。
分布式系统以日志为中心的方案是来自于一个简单的观察,我称之为状态机复制原理(StateMachineReplicationPrinciple):
如果两个相同的、确定性的进程从同一状态开始,并且以相同的顺序获得相同的输入,那么这两个进程将会生成相同的输出,并且结束在相同的状态。
听起来有点难以晦涩,让我们更加深入的探讨,弄懂它的真正含义。
确定性(deterministic)意味着处理过程是与时间无关的,而且不让任何其他『带外数据(outofband)』的输入影响处理结果。
例如,如果一个程序的输出会受到线程执行的具体顺序影响,或者受到getTimeOfDay调用、或者其他一些非重复性事件的影响,那么这样的程序一般被认为是非确定性的。
进程状态 是进程保存在机器上的任何数据,在进程处理结束的时候,这些数据要么保存在内存里,要么保存在磁盘上。
当碰到以相同的顺序输入相同的内容的情况时,应该触发你的条件反射:
这个地方要引入日志。
下面是个很直觉的意识:
如果给两段确定性代码相同的日志输入,那么它们就会生产相同的输出。
应用到分布式计算中就相当明显了。
你可以把用多台机器都执行相同事情的问题化简为实现用分布式一致性日志作为这些处理的输入的问题。
这里日志的目的是把所有非确定性的东西排除在输入流之外,以确保处理这些输入的各个复本(replica)保持同步。
当你理解了这个以后,状态机复制原理就不再复杂或深奥了:
这个原理差不多就等于说的是『确定性的处理过程就是确定性的』。
不管怎样,我认为它是分布式系统设计中一个更通用的工具。
这样方案的一个美妙之处就在于:
用于索引日志的时间戳就像用于保持副本状态的时钟——你可以只用一个数字来描述每一个副本,即这个副本已处理的最大日志记录的时间戳。
日志中的时间戳一一对应了副本的完整状态。
根据写进日志的内容,这个原理可以有不同的应用方式。
举个例子,我们可以记录一个服务的输入请求日志,或者从请求到响应服务的状态变化日志,或者服务所执行的状态转换命令的日志。
理论上来说,我们甚至可以记录各个副本执行的机器指令序列的日志或是所调用的方法名和参数序列的日志。
只要两个进程用相同的方式处理这些输入,这些副本进程就会保持一致的状态。
对日志用法不同群体有不同的说法。
数据库工作者通常说成物理日志(physicallogging)和逻辑日志(logicallogging)。
物理日志是指记录每一行被改变的内容。
逻辑日志记录的不是改变的行而是那些引起行的内容改变的SQL语句(insert、update和delete语句)。
分布式系统文献通常把处理和复制(processingandreplication)方案宽泛地分成两种。
『状态机器模型』常常被称为主-主模型(active-activemodel),记录输入请求的日志,各个复本处理每个请求。
对这个模型做了细微的调整称为『主备模型』(primary-backupmodel),即选出一个副本做为leader,让leader按请求到达的顺序处理请求,并输出它请求处理的状态变化日志。
其他的副本按照顺序应用leader的状态变化日志,保持和leader同步,并能够在leader失败的时候接替它成为leader。
为了理解两种方式的差异,我们来看一个不太严谨的例子。
假定有一个要复制的『算法服务』,维护一个独立的数字作为它的状态(初始值为0),可以对这个值进行加法和乘法运算。
主-主方式所做的可能的是输出所进行的变换的日志,比如『+1』、『*2』等。
各个副本都会应用这些变换,从而经过一系列相同的值。
而主备方式会有一个独立的Master执行这些变换并输出结果日志,比如『1』、『3』、『6』等。
这个例子也清楚的展示了为什么说顺序是保证各副本间一致性的关键:
加法和乘法的顺序的改变将会导致不同的结果。
分布式日志可以看作是建模一致性)(consensus)问题的数据结构。
因为日志代表了『下一个』追加值的一系列决策。
你需要眯起眼睛才能从Paxos)算法簇中找到日志的身影,尽管构建日志是它们最常见的实际应用。
Paxos通过称为multi-paxos的一个扩展协议来构建日志,把日志建模为一系列一致性值的问题,日志的每个记录对应一个一致性值。
日志的身影在ZAB、RAFT和ViewstampedReplication等其它的协议中明显得多,这些协议建模的问题直接就是维护分布式一致的日志。
个人有一点感觉,在这个问题上,我们的思路被历史发展有些带偏了,可能是由于过去的几十年中,分布式计算的理论远超过了其实际应用。
在现实中,一致性问题是有点被过于简单化了。
计算机系统几乎不需要决定单个的值,要的是处理一序列的请求。
所以,日志而不是一个简单的单值寄存器,是更自然的抽象。
此外,对算法的专注掩盖了系统底层所需的日志抽象。
个人觉得,我们最终会更关注把日志作为一个商品化的基石而不是考虑它的实现,就像我们经常讨论哈希表而不纠结于它的细节,比如使用线性探测的杂音哈希(themurmurhashwithlinearprobing)还是某个变种。
日志将成为一种大众化的接口,可以有多种竞争的算法和实现,以提供最好的保证和最佳的性能。
变更日志101:
表与事件的二象性(duality)
让我们继续聊一下数据库。
变更日志和表之间有着迷人的二象性。
日志类似借贷清单和银行处理流水,而数据库表则是当前账户的余额。
如果有变更日志,你就可以应用这些变更生成数据表并得到当前状态。
表记录的是每条数据的最后状态(日志的一个特定时间点)。
可以认识到日志是更基本的数据结构:
日志除了可用来创建原表,也可以用来创建各类衍生表。
(是的,表可以是非关系型用户用的键值数据存储(keyeddatastore)。
)
这个过程也是可逆的:
如果你对一张表进行更新,你可以记录这些变更,并把所有更新的『变更日志(changelog)』发布到表的状态信息中。
这些变更日志正是你所需要的支持准实时的复制。
基于此,你就可以清楚的理解表与事件的二象性:
表支持了静态数据,而日志记录了变更。
日志的魅力就在于它是变更的完整记录,它不仅仅包含了表的最终版本的内容,而且可以用于重建任何存在过其它版本。
事实上,日志可以看作是表每个历史状态的一系列备份。
这可能会让你想到源代码的版本控制(sourcecodeversioncontrol)。
源码控制和数据库之间有着密切的关系。
版本管理解决了一个和分布式数据系统要解决的很类似的问题——管理分布式的并发的状态变更。
版本管理系统建模的是补丁序列(thesequenceofpatches),实际上这就是日志。
你可以检出当前的代码的一个『快照』并直接操作,这个代码快照可以类比成表。
你会注意到,正如有状态的分布式系统一样,版本控制系统通过日志来完成复制:
更新代码即是拉下补丁并应用到你的当前快照中。
从销售日志数据库的公司Datomic那里,大家可以看到一些这样的想法。
这个视频比较好地介绍了他们如何在系统中应用这些想法。
当然这些想法不是只专属于这个系统,这十多年他们贡献了很多分布式系统和数据库方面的文献。
这节的内容可能有点理论化了。
但别沮丧!
后面马上就是实操的干货。
接下来的内容
本文剩下的内容,我会试着重点讲述,除了作为分布式计算内部实现或模型抽象,日志有什么优点。
包含:
1.数据集成(DataIntegration)——让组织中所有存储和处理系统可以容易地访问组织所有的数据。
2.实时数据处理 ——计算生成的数据流。
3.分布式系统设计 ——如何通过集中式日志的设计来简化实际应用系统。
所有这些用法都是通过把日志用做单独服务来实现的。
上面这些场景中,日志的好处都来自日志所能提供的简单功能:
生成持久化的可重放的历史记录。
令人意外的是,能让多台机器以确定性的方式(deterministicmanner)按各自的速度重放历史记录的能力是这些问题的核心。
(2)数据集成
我先解释一下我说的是『数据集成』(dataintegration)是什么,还有为什么我觉得它很重要,然后我们再来看看它是如何和日志建立关系的。
数据集成是指使一个组织的所有数据对这个组织的所有的服务和系统可用。
『数据集成』还不是一个常见的用语,但是我找不到一个更好的。
大家更熟知的术语ETL (译注:
ETL是Extraction-Transformation-Loading的缩写,即数据提取、转换和加载)通常只是覆盖了数据集成的一个有限子集——主要在关系型数据仓库的场景。
但我描述的东西很大程度上可以理解为,将ETL推广至覆盖实时系统和处理流程。
你一定不会听到数据集成就兴趣盎然地屏住呼吸,并且天花乱坠的想到大数据的概念,但尽管如此,我相信这个陈词滥调的『让数据可用』的问题是组织可以关注的更有价值的事情之一。
对数据的高效使用遵循一种马斯洛的需要层次理论。
金字塔的基础部分包含捕获所有相关数据,能够将它们全部放到适当的处理环境中(可以是一个华丽的实时查询系统,或仅仅是文本文件和python脚本构成的环境)。
这些数据需要以统一的方式建模,以方便读取和处理。
一旦这些以统一的方式捕获数据的基本需求得到满足,那么在基础设施上以不同方法处理这些数据就变得理所当然—— MapReduce、实时查询系统等等。
显而易见但值得注意的一点:
如果没有可靠的、完整的数据流,Hadoop集群只不过是个非常昂贵而且安装麻烦的供暖器。
一旦有了数据和处理(dataandprocessing),人们的关注点就会转移到良好的数据模型和一致且易于理解的语义这些更精致的问题上来。
最后,关注点会转移到更高级处理上——更好的可视化、生成报表以及处理和预测算法。
以我的经验,大多数组织在这个数据金字塔的底部存在巨大的漏洞——缺乏可靠的完整的数据流——却想直接跳到高级数据模型技术上。
这样做完全是本未倒置。
所以问题是,我们如何在组织中构建贯穿所有数据系统的可靠数据流?
数据集成:
两个难题
有两个趋势使数据集成变得更加困难。
事件数据管道
第一个趋势是增长的事件数据(eventdata)。
事件数据记录的是发生的事情,而不是已存在的事情。
在Web系统中,这就意味着用户活动日志,还有为了可靠地操作和监控数据中心机器的价值而记录的机器级别的事件和统计数字。
人们倾向于称它们为『日志数据(logdata)』,因为它们经常被写到应用日志中,但这样的说法混淆了形式与功能。
这些数据是现代Web的核心:
归根结底,Google的财富来自于建立在点击和展示(clicksandimpressions)上的相关性管道(relevancepipeline),而这些点击和展示正是事件。
这样的事情并不是仅限于Web公司,只是Web公司已经完全数字化,所以更容易完成。
财务数据长久以来一直是以事件为中心的。
RFID(无线射频识别)使得能对物理设备做这样的跟踪。
随着传统的业务和活动的数字化(digitization),我认为这个趋势仍将继续。
这种类型的事件数据记录了发生的事情,往往比传统数据库应用要大好几个数量级。
这对于处理提出了重大的挑战。
专用的数据系统(specializeddatasystems)的爆发
第二个趋势来自于专用的数据系统的爆发,这些数据系统在最近五年开始流行并且可以免费获得。
专门用于OLAP、搜索、简单 在线 存储、 批处理、图分析graphanalysis 等 等 的数据系统已经出现。
更加多样化的数据同时变成更加大量,而且这些数据期望放到更多的系统中,这些需求同时要解决,导致了一个巨大的数据集成问题。
日志结构化的(log-structured)数据流
处理系统之间的数据流,日志是最自然的数据结构。
解决方法很简单:
提取所有组织的数据,并放到一个用于实时订阅的中心日志中。
每个逻辑数据源都可以建模为它自己的日志。
一个数据源可以看作一个输出事件日志的应用(如点击或页面的浏览),或是一个接受修改的数据库表。
每个订阅消息的系统都尽可能快的从日志读取信息,将每条新的记录应用到自己的存储中,同时向前滚动日志文件中的自己的位置。
订阅方可以是任意一种数据系统——缓存、Hadoop、另一个网站中的另一个数据库、一个搜索系统,等等。
举个例子,日志概念为每个变更提供了逻辑时钟,所有的订阅方都可以比较这个逻辑时钟。
这极大简化了如何去推断不同的订阅系统的状态彼此是否一致的,因为每个系统都持有了一个读到哪儿的『时间点』。
为了让讨论更具体些,我们考虑一个简单的案例,有一个数据库和一组缓存服务器集群。
日志提供了一个方法可以同步更新到所有这些系统,并推断出每个系统的所处在的时间点。
我们假设做了一个写操作,对应日志记录X,然后要从缓存做一次读操作。
如果我们想保证看到的不是过时的数据,我们只需保证,不要去读取那些复制操作还没有跟上X的缓存即可。
日志也起到缓冲的作用,使数据的生产异步于数据的消费。
有许多原因使得这一点很重要,特别是在多个订阅方消费数据的速度各不相同的时候。
这意味着一个数据订阅系统可以宕机或是下线维护,在重新上线后再赶上来:
订阅方可以按照自己的节奏来消费数据。
批处理系统如Hadoop或者是一个数据仓库,或许只能每小时或者每天消费一次数据,而实时查询系统可能需要及时到秒。
无论是起始的数据源还是日志都感知感知各种各样的目标数据系统,所以消费方系统的添加和删除无需去改变传输管道。
特别重要的是:
目标系统只知道日志,而不知道来源系统的任何细节。
无论是数据来自于一个RDBMS、一种新型的键值存储,还是由一个不包含任何类型实时查询的系统所生成的,消费方系统都无需关心。
这似乎是一个小问题,但实际上却是至关重要的。
『每个工作的数据管道要设计得像是一个日志;每个损坏的数据管道都以其自己的方式损坏。
』
—— CountLeoTolstoy (由作者翻译)
这里我使用术语『日志』取代了『消息系统』或者『发布-订阅』,因为在语义上明确得多,并且准确得多地描述了在实际实现中支持数据复制时你所要做的事。
我发现『发布订阅』只是表达出了消息的间接寻址(indirectaddressingofmessages)——如果你去比较两个发布-订阅的消息系统的话,会发现他们承诺的是完全不同的东西,而且大多数模型在这一领域没什么用。
你可以认为日志是一种有持久性保证和强有序(strongordering)语义的消息系统。
在分布式系统中,这个通信模型有时有个(有些可怕的)名字叫做原子广播(atomicbroadcast)。
值得强调的是,日志仍然只是基础设施,并不是精通数据流这个故事的结束:
故事的剩余部分围绕着元数据(metadata)、schemas、兼容性以及处理数据结构及其演化的所有细节来展开。
但是,除非有一种可靠的通用的方式来处理数据流的机制,否则语义细节总是次要的。
在LinkedIn
随着LinkedIn从集中式关系数据库过渡到一套分布式系统,我注意到数据集成的问题在迅速地演变。
目前我们主要的数据系统包括:
∙搜索
∙SocialGraph
∙Voldemort (键值存储)
∙Espresso (文档存储)
∙推荐引擎
∙OLAP查询引擎
∙Hadoop
∙Terradata
∙Ingraphs (监控图表和指标服务)
每一个都是专用的分布式系统,在各自的专门领域提供高级的功能。
使用日志作为数据流的这个想法,甚至在我到这里之前,就已经在LinkedIn的各个地方开始浮现了。
我们开发的最早的一个基础设施是一个称为databus的服务,它在我们早期的Oracle表上提供了一种日志缓存的抽象,用于可伸缩地订阅数据库修改,给我们的socialgraph和搜索索引输入数据。
我先简单介绍一些历史以提供讨论的上下文。
在发布我们自己键值存储之后,大约是2008年我开始参与这个项目。
我接着的一个项目是把一个运行的Hadoop部署用起来,迁移我们的一些推荐处理上来。
由于缺乏这方面的经验,我们只计划了几周时间完成数据的导入导出,剩下的时间则用来实现复杂的预测算法。
就这样我们开始了长途跋涉
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- The Log每个程序员都应该知道有关实时数据的统一抽象 Log 每个 程序员 应该 知道 有关 实时 数据 统一 抽象