热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

日志:每个软件工程师都应该知道的实时数据的一致抽象-第一部分

原文:点击打开链接自己翻译的,发现网上已经有中文版了…。有些地方是意译,理解不到位的地方还请包涵。我于六年前在一个特别有趣的时间点加入LinkedIn,那时

原文:点击打开链接

自己翻译的,发现网上已经有中文版了…。

有些地方是意译,理解不到位的地方还请包涵。


我于六年前在一个特别有趣的时间点加入LinkedIn,那时候我们正面临单片、中心化数据库的限制,并需要开始向专门的分布式系统组合转型。这是一个有趣的经历:我们构建、发布、并运行至今一个分布式图形数据库,一个分布式搜索后端,一个Hadoop应用,和一个一代、二代key-value存储系统。

从中我学到的最有用的一件事是,我们构建的许多系统,其核心都有一个非常简单的概念:日志。有时候被称作“前写日志”,或“提交日志”,或“事务日志”。日志伴随计算机系统而存在,也是许多分布式数据系统和实时计算应用架构的核心。

不理解日志,你就无法透彻的理解数据库,NoSQL存储,key-value存储,复制,paxos算法,Hadoop,版本控制(version control),或几乎所有软件系统。但很多软件工程师却并不熟悉它们,我希望改变这个现状。在这篇文章里,我将向你展示你必须了解的有关日志的一切,包括什么是日志,及如何在数据集成、实时计算和系统构建中使用日志。

第一部分:什么是日志?
日志可能是最简单的存储抽象。它是只增的,严格按照时间顺序排列的记录序列,看起来就像:

记录被添加到日志的结尾,从左到右依次读取。每个条目都被分配一个唯一的连续的日志编号。

记录的顺序定义了“时序”的概念,因为左边的条目被定义为早于右边的条目。条目的日志编号可以认为是条目的“时间戳”。将此种顺序描述为时间流的概念,一开始看起来有点古怪,但是它具备同任何特别的物理时钟解耦的方便特性。在我们接触到分布式系统时,该特性将变得十分必要。

记录的内容和格式对于本文讨论的目的来说不重要。同时,因为存储空间的限制,我们不能一直不停的添加记录到日志。稍后我们还会讲到这个问题。

日志与文件或表并不是那么的不同。文件是字节的一个数组,表是记录的一个数组,而日志本质上就是其记录按照时间排序的文件或表。

此时你可能在想:如此简单的事情有什么值得讨论的?一个记录的只增序列怎么会跟数据系统有关系?答案是,日志有个明确的用途:记录何时发生了何事。对于分布式系统来说,在很多情况下,这就是问题的核心。

在我们扯远之前,让我先来澄清一件有点让人混淆的事情。每个程序员都很熟悉日志的另外一种定义——应用可能使用log4j或syslog写入本地文件的非结构化的错误信息或追踪信息,为了明晰起见,我叫这个做“应用日志”。应用日志是是我所描述的的日志概念的退化版本。最大的不同是:文本日志应该主要提供给人阅读,而我所描述的“日志”或“数据日志”则为程序可读而建。

(实际上,你想想看,由人来通读个别机器上的日志的想法本身就与时代不符。当很多服务和系统牵涉其中,日志的用途很快就变成了查询和图形化理解机器间行为的输入,这种做法很快就变成无法管理的策略——文件中的文本记录完全不如此处描述的结构化日志合适。)

数据库中的日志
我不知道日志概念起源何处——可能它就像二进制搜索一样太过简单,以至于发明者根本没有意识到它是一项发明。在最早的IBM R系统(IBM's System R)中就存在日志了。使用数据库系统必须要在系统存在崩溃可能的情况下,处理不同类型数据结构和索引的同步。为了使该操作原子化和持久化,在将变更应用到数据库维护的所有不同数据结构之前,数据库使用日志记录将要被修改的记录的信息。日志是事件发生的记录,每个表或索引都是这个历史记录到某种有用数据结构或索引的投影。由于日志是立即持久化的,因此它被用作在系统崩溃后恢复所有其他持久化结构的权威源头。

随着时间的推移,日志被更多的使用,从ACID的实现细节,到数据库之间的数据复制方法都用到了日志。事实证明一个数据库上发生的变更序列,恰恰是需要保持同步的远端镜像数据库所需的。Oracle、MySQL和PostgreSQL引入日志传输协议,将日志块传输给作为slave的镜像数据库。Oracle还把日志产品化为一个通用的数据订阅机制,这样非Oracle数据订阅用户就可以使用XStreams和GoldenGate订阅数据了,MySQL和PostgreSQL上的类似的实现则成为许多数据结构的关键组件。(后一句参考了网上的另一篇翻译)

因为这个缘起,机器可读日志的概念在很大程度上被限于数据库内部。使用日志作为数据订阅机制的兴起看起来似乎有点偶然。但是这种高度抽象对于支持所有类型的消息、数据流、实时数据处理来说很理想。

分布式系统中的日志
日志解决的两个问题:顺序化变更和分发数据,在分布式数据系统中尤为重要。对一组更新序列达成一致(或对否决达成一致,并应对副作用)是这些系统设计的核心问题之一。

以日志为中心的分布式系统源起于对一个准则的解读,我将这个准则称为“状态机复制准则”:
如果两个同样的,确定的进程开始于同样的状态,并且以相同的顺序获得相同的输入,它们将产生一致的结果,并以相同的状态结束。
这看起来有点绕,让我们来深入理解一下它的含义。

确定性意味着程序处理不是时间依赖的,并且不会让任何“越界”的输入影响结果。举例来说,如果一个程序的输出依赖执行线程的特别顺序,或调用类似gettimeofday等其它不可重复获取值的方法,一般最好认为它是不确定性的。

状态是程序处理结束后,机器 内存中或磁盘中留存数据的任何形式。

以相同的顺序获取相同的指令这点应当引起你的注意——这就是引入日志的地方。这是一个非常直觉的观念:如果你给两个确定性的程序片段提供相同的日志输入,它们将产生相同的输出。

分布式计算的程序设计是显而易见的,通过实现分布式一致性日志,并将其作为处理相同问题的复数机器的输入,来分解问题。在这里,日志的目的是将所有的不确定性从输入流中“挤”出去,来确保每个处理该输入的复制进程保持同步。

当你理解了这点,就没有关于该准则更复杂或深入的内容了:它差不多是说“确定性的处理是确定性的”,尽管如此,我认为它是设计分布式系统的更加通用的工具之一。

这种方式很美好的一个地方是:索引日志的时间戳作为复制状态的时钟——你可以通过一个简单的数字描述每个复制机,这个数字来自复制机已经处理的最大日志条目的时间戳。这个时间戳和日志组合起来,唯一地捕获了复制机的完整状态。

根据日志中写入的内容不同,有大量的方法在系统上实现该准则。比如,我们可以记录到达的对服务的请求,或者服务为了做出响应而执行的对状态的修改,或者服务执行的转换命令。理论上,我们甚至可以记录在每个复制机上执行的机器指令,或方法名及参数序列。只要两个进程以同样的方式处理这些输入,复制机群间的处理进程(状态)就能够保持一致。

不同的人群对于日志的用途有不同的描述。数据库用户一般区别对待物理日志和逻辑日志。物理日志是记录被变更的每一行的内容,逻辑日志是记录导致行数据变更的SQL(INSERT、UPDATE、DELETE语句)。 --xingrk译注:行复制、逻辑复制;

分布式系统文献大多将两个宽泛的模式区分为处理和复制。“状态机模型”一般是指“ 主动-主动模型 ”,在此模式下我们维护一个到达请求的日志,每个复制机处理每个请求(?)。该模式的一个修改版本,称作“主动-备动模型”,是选举一个复制机作为主机,并让主机按照请求到达的顺序处理它们,同时记录因处理请求引起的状态变化日志。其它复制机顺序应用主机产生的状态变化,从而保持同步,并准备作为新的主机接管可能失效的当前主机。

为了理解这两种方式的不同,想象一个做镜像的“算法服务”,它维护一个值及其状态(初始化为0),并对该值应用加法和乘法操作。“主动-主动模式”会记录所应用的转换操作,如“+1”,“*2”等。每个复制机都将应用这些转换操作,因此会经历同样的值变化。“主动-被动模式”会让一台主机执行转换操作并记录结果,如“1”,“3”,“6”等(译注:复制机直接同步结果)。这个例子还说明了为什么顺序是复制机之间保证一致性的关键:重排一个加法和乘法操作将导致不同的结果。

分布式日志可以看成是对共识问题建模的数据结构。日志代表了对“下一个”添加值的一系列决定。必须仔细分析才能从Paxos算法族中看到日志的存在,尽管构建日志是它们最常见的应用实践。根据Paxos算法,这通常是使用叫做“multi-paxos”的协议的一个扩展来实现,它将日志建模为一系列的共识问题,一一对应到日志中的每个槽位。在ZAB、RAFT或Viewstamped Replication等其它协议中,日志的作用更加突出,是直接对维护分布的一致性日志问题进行建模的。

我的怀疑是,因为历史的原因我们对问题的看法有些偏颇,可能要归因于近几十年分布式计算理论的发展比实践应用要快。现实中的共识问题有点太简单。计算机系统很少需要决定一个单一的值,通常都需要处理一个请求队列。因此,相对一个简单的单值寄存器,日志是更自然的抽象。

此外,对于算法的关注模糊了系统需要的底层日志抽象。我怀疑我们最终会更加关注日志作为一个商品化的构建模块而忽视它的实现,就像我们经常谈论哈希表却不愿深入底层去了解我们随口说的哈希是线性探查或其他变体(hash算法实现?)。日志成为大众化的接口,会有多种算法和实现竞相提供最好的保证和最佳的性能。

变更日志101:表与事件的二相性
让我们再来看看数据库。数据库中的变更日志和表有着很好的二相性。日志就像是银行处理的信贷和借记列表;表是所有当前帐户的结余。如果有变更日志,通过顺序应用变更操作, 就可以创建表 来捕获当前系统的状态。表会记录每个键值最后的状态(来自某个特定的日志时间)。一种看法是,日志是更基础的数据结构:除了创建原始表,还可以对其转换以创建所有类型的衍生表(是的,在非关系型数据库分支中,表也可以是键值存储)。

这个流程也是可逆的:如果有一个表接受更新操作,可以记录这些变更并发布一个包含了所有对表的状态做更新的操作的 “变更日志。”这个日志正是用来支持准实时复制所需要的。因此从这个观念当中可以看到表和事件是二相的:表维护静态的数据,而日志捕获变化。日志的灵活性体现在,如果它是一个完整的变更日志,那么它不仅持有最终版本的表的内容,还能够支持重建所有其它可能存在的表的版本。这是对表的每一种历史状态的有效备份方式。

这可能会让人想起对源代码的版本控制。在源代码控制和数据库之间有着很强的联系。版本控制解决了跟分布式数据库系统必须解决的问题相类似的问题——管理分布的、并发的状态变化。版本控制系统通常会以补丁序列进行建模,实际上就是日志。跟表类似,我们直接跟检出的当前代码的“快照”进行交互。注意到在版本控制系统,及其它分布的有状态的系统中,复制通过日志完成:当执行更新的时候,将补丁拉下来并应用到系统当前快照上。

最近一些人可能从Datomic公司(销售一个以日志为中心的数据库产品)看过一些相关的理念。这个报告给出了他们是如何将这些理念应用到他们的系统中的概述。当然,这些理念不单单只适用于这个系统,就像它们作为分布式系统和数据库系统文献的一个部分数十年之久。

这些看起来有点过于理论化。不要失望!很快我们就会接触到应用的部分。

接下来
在本文剩下的部分,我将尝试通俗的说明当涉及到跨越分布式计算或抽象的分布式计算模型内部时,日志是如何发挥作用的。这包括:
1、数据集成——让组织的所有数据在其所有存储和处理系统之间方便的共享;
2、实时数据处理——计算获取数据流;
3、分布式系统设计——以日志为中心的设计是如何简化可行的系统的;

这些都是围绕着将日志作为独立服务的理念来确定的。

在每个部分,日志系统从提供最简单的记录开始:生成持久的,可重放的历史记录。意外的是,这些问题的核心是让多台服务器以它们各自的速度和确定性的方式重放历史记录的能力。



推荐阅读
  • 本文深入探讨了NoSQL数据库的四大主要类型:键值对存储、文档存储、列式存储和图数据库。NoSQL(Not Only SQL)是指一系列非关系型数据库系统,它们不依赖于固定模式的数据存储方式,能够灵活处理大规模、高并发的数据需求。键值对存储适用于简单的数据结构;文档存储支持复杂的数据对象;列式存储优化了大数据量的读写性能;而图数据库则擅长处理复杂的关系网络。每种类型的NoSQL数据库都有其独特的优势和应用场景,本文将详细分析它们的特点及应用实例。 ... [详细]
  • 本文详细介绍了Java代码分层的基本概念和常见分层模式,特别是MVC模式。同时探讨了不同项目需求下的分层策略,帮助读者更好地理解和应用Java分层思想。 ... [详细]
  • DAO(Data Access Object)模式是一种用于抽象和封装所有对数据库或其他持久化机制访问的方法,它通过提供一个统一的接口来隐藏底层数据访问的复杂性。 ... [详细]
  • 网站访问全流程解析
    本文详细介绍了从用户在浏览器中输入一个域名(如www.yy.com)到页面完全展示的整个过程,包括DNS解析、TCP连接、请求响应等多个步骤。 ... [详细]
  • 从0到1搭建大数据平台
    从0到1搭建大数据平台 ... [详细]
  • 本文详细介绍了数据库并发控制的基本概念、重要性和具体实现方法。并发控制是确保多个事务在同时操作数据库时保持数据一致性的关键机制。文章涵盖了锁机制、多版本并发控制(MVCC)、乐观并发控制和悲观并发控制等内容。 ... [详细]
  • MySQL的查询执行流程涉及多个关键组件,包括连接器、查询缓存、分析器和优化器。在服务层,连接器负责建立与客户端的连接,查询缓存用于存储和检索常用查询结果,以提高性能。分析器则解析SQL语句,生成语法树,而优化器负责选择最优的查询执行计划。这一流程确保了MySQL能够高效地处理各种复杂的查询请求。 ... [详细]
  • 在当今的软件开发领域,分布式技术已成为程序员不可或缺的核心技能之一,尤其在面试中更是考察的重点。无论是小微企业还是大型企业,掌握分布式技术对于提升工作效率和解决实际问题都至关重要。本周的Java架构师实战训练营中,我们深入探讨了Kafka这一高效的分布式消息系统,它不仅支持发布订阅模式,还能在高并发场景下保持高性能和高可靠性。通过实际案例和代码演练,学员们对Kafka的应用有了更加深刻的理解。 ... [详细]
  • 如果应用程序经常播放密集、急促而又短暂的音效(如游戏音效)那么使用MediaPlayer显得有些不太适合了。因为MediaPlayer存在如下缺点:1)延时时间较长,且资源占用率高 ... [详细]
  • importpymysql#一、直接连接mysql数据库'''coonpymysql.connect(host'192.168.*.*',u ... [详细]
  • Spark中使用map或flatMap将DataSet[A]转换为DataSet[B]时Schema变为Binary的问题及解决方案
    本文探讨了在使用Spark的map或flatMap算子将一个数据集转换为另一个数据集时,遇到的Schema变为Binary的问题,并提供了详细的解决方案。 ... [详细]
  • 秒建一个后台管理系统?用这5个开源免费的Java项目就够了
    秒建一个后台管理系统?用这5个开源免费的Java项目就够了 ... [详细]
  • 通过使用Sqoop导入工具,可以精确控制并高效地将表数据的特定子集导入到HDFS中。具体而言,可以通过在导入命令中添加WHERE子句来指定所需的数据范围,从而在数据库服务器上执行相应的SQL查询,并将查询结果高效地存储到HDFS中。这种方法不仅提高了数据导入的灵活性,还确保了数据的准确性和完整性。 ... [详细]
  • MySQL Decimal 类型的最大值解析及其在数据处理中的应用艺术
    在关系型数据库中,表的设计与SQL语句的编写对性能的影响至关重要,甚至可占到90%以上。本文将重点探讨MySQL中Decimal类型的最大值及其在数据处理中的应用技巧,通过实例分析和优化建议,帮助读者深入理解并掌握这一重要知识点。 ... [详细]
  • 在使用达梦数据库时,管理员可能会遇到连接频繁中断或特定SQL语句语法错误的问题。这些问题通常源于开发人员在创建对象时的不规范操作。为了解决这些问题,建议对数据库配置进行优化,并确保所有SQL语句符合达梦数据库的标准语法。此外,定期检查和维护数据库连接参数,以及对异常日志进行详细分析,也有助于及时发现并解决问题。 ... [详细]
author-avatar
miss文女1977
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有