这是一篇学习笔记。
学习的材料来自Jay Kreps的一篇讲Log的博文。
原文很长,可是我坚持看完了,收获颇多,也深深为Jay哥的技术能力、架构能力和对于分布式系统的理解之深入所折服。同时也由于某些理解和Jay哥观点吻合而略沾沾自喜。java
Jay Kreps是前Linkedin的Principal Staff Engineer,现任Confluent公司的联合创始人和CEO,Kafka和Samza的主要做者。python
所谓笔记,就是看了文章,提笔就记,由于Jay哥自己本章组织的太好,而其自己的科学素养及哲学素养也很高,因此私觉得出彩的东西就不省略了。程序员
The Log: What every software engineer should know about real-time data's unifying abstraction算法
1) Log是以下系统的核心:数据库
2) Log可能跟计算机的历史同样长,而且是分布式数据系统和实时计算系统的核心。
3) Log的名字不少:编程
4) 不理解Log,你就不可能充分理解缓存
记录的顺序定义了这样的一个概念:时间。
由于越靠左的记录越早。
Entry的序号能够看成一种时间戳,将记录的顺序看成时间这一律念看起来很奇怪,可是很快你就会发现,这样作:能够方便地将“时间”与任一特定的物理时钟解耦。
Log和常见的文件、表(table)没有那么大的差异。安全
这样说,可能你会以为log如此简单,还有讨论的必要吗?
其实,log的核心意义在于:服务器
Log记录了什么时候发生了什么(they record what happened and when.)。
而这一条,一般是分布式系统最最最核心的东西。
注意,这里有必要澄清几个概念:网络
Log的起源不得而知,就像发明二分查找的人,难以意识到这种发明是一种发明。
Log的出现和IBM的System R 同样早。
在数据库中,须要在数据库崩溃时,保持多种多样的数据结构和索引保持同步。
为保证原子性和持久性,数据库须要在对数据结构和索引进行修改提交以前,记录其要修改的内容。
因此log记录了什么时候发生了什么,而每一张表和索引自己,都是这种历史信息的映射。
由于log是当即持久化的,因此当crash发生时,其成为恢复其它持久化结构的可靠来源。
Log从保证ACID特性的一种实现,发展成了一种数据库之间数据复制的手段。
很显然,数据库中发生的一系列的数据变动,成为数据库之间 保持同步最须要的信息。
Oracle、MySQL、PostgreSQL,都包含了log传输协议,将log的一部分发送到用于保持复制的从数据库(Slave)。
Oracle的XStreams和GoldenState,将log看成一种通用的数据订阅机制,以提供给非Oracle的数据库订阅数据。
MySQL和PostgreSQL也提供了相似的组件,这些组件是数据系统架构的核心。
面向机器的Log,不只仅可被用在数据库中,也能够用在:
Log解决了两个很重要的分布式数据系统中的问题:
1) 有序的数据变化
2) 数据分布式化
所谓的状态机复制原理(State Machine Replication Principle):
若是两个肯定的处理过程,从相同的状态开始,按照相同的顺序,接收相同的输入,那么它们将会产生相同的输出,并以 相同的状态结束。
所谓肯定的(deterministic),是指处理过程是时间无关的,其处理结果亦不受额外输入的影响。
能够经过非肯定的例子来理解:
所谓状态,能够是机器上的任意数据,不管在处理结束后,是在机器的内存中仍是磁盘上。
相同的输入按照相同的顺序,产生相同的结果,这一点值得引发你的注意,这也是为何log会如此重要,这是一个直觉性的概念:若是你将同一个log输入两个肯定性的程序,它们将产生相同的输出。
在分布式系统的构建中,意识到这一点,可使得:
让全部的机器作一样的事,规约为:
构建分布式的、知足一致性的log系统,觉得全部处理系统提供输入。
Log系统的做用,就是将全部的输入流之上的不肯定性驱散,确保全部的处理相同输入的复制节点保持同步。
这种方法的最妙之处在于,你能够将索引日志的时间戳,做为全部复制节点的时钟来对待:
经过将复制节点所处理过的log中最大的时间戳,做为复制节点的惟一ID,这样,时间戳结合log,就能够惟一地表达此节点的整个状态。
应用这种方法的方式也不少:
理论上来说,咱们能够记录一系列的机器指令,或者所调用方法的名称及参数,只要数据处理进程的行为相同,这些进程就能够保证跨节点的一致性。
常玩儿数据库的人,会将逻辑日志和物理日志区分对待:
对分布式系统,一般有两种方式来处理复制和数据处理:
1) State machine model(active - active)
2) Primary-back model (active - passive)
以下图所示:
为了理解上述两种方式的不一样,来看个简单的例子:
如今,集群须要提供一个简单的服务,来作加法、乘法等算术运算。初始,维护一个数字,好比0。
上面的例子也揭示了,为何顺序是复制节点之间保持一致性的关键因素,若是打乱了这些操做的顺序,就会获得不一样的运算结果。
分布式log,能够当作某些一致性算法的数据结构:
一条log,表征了一系列的关于下一个值是什么的决定。
从数据库的角度来看,一组记录数据变化的changelog和表,是对偶和互通的。
1) 依据记录了数据变化的log,能够重构某一状态的表(也能够是非关系型存储系统中有key的记录)
2) 相反,表若是发生了变化,能够将变化计入log。
这正是你想要的准实时复制的秘籍所在!
这一点和版本控制所作的事情极为相似:管理分布式的、并发的、对状态进行的修改。
版本控制工具,维护了反映修改的补丁,这其实就是log,你和一个被签出(checked out)的分支快照进行交互,这份快照就至关于数据库中的表。你会发现,版本控制与分布式系统中,复制都是基于log的:当你更新版本时,你只是拉取了反映了版本变化的补丁,并应用于当前的分支快照。
所谓数据集成,就是将一个组织中的全部服务和系统的数据,变得可用。
实际上,对数据进行有效利用,很符合马斯洛的层次需求理论。
金字塔的最底层,是收集数据,将其整合进应用系统中(不管是实时计算引擎,仍是文本文件,仍是python脚本)。
而这些数据,须要通过转换,保持一个统1、规范、整洁的格式,以易于被读取和处理。
当上面的要求被知足后,就能够开始考虑多种多样的数据处理方式,好比map – reduce 或者实时查询系统。
很显然,若是没有一个可靠的、完备的数据流,Hadoop就仅仅是一个昂贵的、难以整合的加热器(集群很费电么?)。
相反,若是能保证数据流可靠、可用且完备,就能够考虑更高级的玩法、更好的数据模型和一致的、更易被理解的语义。
接着,注意力就能够转移到可视化、报表、算法和预测上来(挖啊机啊深度啊)。
事件
事件数据,记录了事件是怎么发生的,而不只仅是发生了什么,这一类log一般被当作应用日志,由于通常是由应用系统写入的。但这一点,其实混淆了log的功能。
Google的财富,其实,是由一个创建在(用户)点击流和好恶印象(体验)之上的相关性pipeline产生的,而点击流和印象,就是事件。
各类各样的专业数据系统的爆发
这些系统存在的缘由:
显然,要将数据整合进这样的系统中,对于数据集成来说,极为困难。
每种逻辑意义上的数据源,均可以依据log进行建模。
数据源能够是记录了事件(点击和PV)的应用程序,能够是接受更改的数据库表。
每一个订阅者,都尽量快地从这些数据源产生的log中获取新的记录,应用于本地的存储系统,而且提高其在log中的读取偏移(offset)。订阅者能够是任何数据系统,好比缓存、Hadoop、另外一个站点的数据库,或者搜索引擎。
Log,实际上提供了一种逻辑时钟,针对数据变化,能够测量不一样的订阅者所处的状态,由于这些订阅者在log中的读取偏移不一样且相互独立,这种偏移就像一个时间意义上的“时刻”同样。
考虑这样一个例子,一个数据库,和一些缓存服务器:
Log提供了这样一种能力,可使得全部的缓存服务器获得同步,并推出它们所处的“时刻”。
假设咱们写入了一个编号为X的log,要从某个缓存服务器读取数据,为了避免读到老数据,只须要保证:在缓存服务器将数据(同步)复制到X这个位置前,咱们不从这个缓存中读取任何东西便可。
此外,log还提供了做为缓冲区的能力,以支持生产者和消费者的行为以异步的方式进行。
最关键的一个支持异步的缘由,是订阅系统可能会发生崩溃、因维护而下线,接着恢复上线,而在这种状况下,每一个订阅者都以本身的步调消费数据。
一个批处理系统,好比Hadoop,或者一个数据仓库,是以小时或天为单位消费数据,而一个实时系统,一般在秒级消费数据。
而数据源或者log,对消费数据的订阅者一无所知,因此,须要在pipeline中作到无缝的添加订阅者和移除订阅者。
更重要的是,订阅者,只须要知道log,而不须要对其所消费的数据的来源有任何了解,不管这个数据源是RDBMS、Hadoop,仍是一个最新流行的K-V数据库,等等。
之因此讨论log,而不是消息系统,是由于不一样的消息系统所保证的特性不一样,而且用消息系统这个词,难以全面和精确表达某种语义,由于消息系统,更重要的在于重定向消息。
可是,能够将log理解为这样一种消息系统,其提供了持久性保证及强有序的语义,在通信系统中,这称做原子广播。
Linkedin目前的主要系统包括(注:2013年):
每一个系统,都在其专业的领域提供专门的高级功能。
(这一段太长太长了,Jay兄十分能侃啊,因此挑重点的来记吧!)
1) 之因此引入数据流这个概念,是由于要在oracle数据库的表之上,创建一个抽象的缓存层,为搜索引擎的索引构建和社交图谱更新,提供拓展能力。
2) 为了更好的处理linkedin的一些推荐算法,开始搭Hadoop集群,但团队在此块的经验尚浅,因此走了不少弯路。
3) 开始时,简单粗暴地认为只要将数据从oracle数据仓库中拉出来,丢进hadoop就能够了。结果发现:第一,将数据从oracle数据仓库快速导出是个噩梦;第二,也是更糟糕的一点,数据仓库中某些数据的处理不对,致使了hadoop的批处理任务不能按预期输出结果,且经过hadoop批处理执行任务,一般不可逆,特别是在出了报表以后。
4) 最后,团队抛弃了从数据仓库中出数据的方式,直接以数据库和logs为数据源。接着,造出了一个轮子:K-V 存储(Voldemort)。
5) 即便是数据拷贝这样不高大上的活儿,也占据了团队大量的时间去处理,更糟的是,一旦数据处理的pipeline中有个点出错,hadoop立马变得废柴,由于再牛逼的算法跑在错误的数据上,只有一个后果,就是产生更多的错误数据。
6) 即便团队构建的东西抽象层次很高,针对每种数据源仍是须要特定的配置,而这也是不少错误和失败的根源。
7) 一大批程序员想跟进,每一个程序员都有一大批的想法,集成这个系统,添加这个功能,整合这个特点,或者想要自定义的数据源。
8) Jay哥开始意识到:
第一, 虽然他们构建的pipelines还很糙,可是却极其有价值。即便是解决了数据在新的系统(如hadoop)中可用的问题,也解锁了一大批可能性。之前难作的计算开始变为可能。新的产品和分析,仅须要解锁其它系统中的数据,而且进行整合,就能够容易地作出来。
第二, 很明显,可靠地数据装载须要更坚实的支撑,若是可以捕获全部的结构,就可让hadoop数据装载彻底自动化,不须要加入新的数据源或人工修改数据的模式。数据会神奇地出如今HDFS中,而新的数据源加入后,Hive的表会用合适的列自动化地、自适应地生成。
第三,数据覆盖度远远不足。由于要处理不少新的数据源,很难。
9) 为了解决新数据源加入后的数据装载问题,团队开始了这样的尝试:
很快,他们发现这样搞行不通,由于发布和订阅、生产和消费,数据流一般仍是双向的,这成了一个O(n^2)的问题。
因此,他们须要的是这样的模型:
须要将每一个消费者从数据源隔离,理想的状况下,这些消费者只和一个data repository进行交互,而这个repository能够提供它们访问任意数据的能力。
10)消息系统 + log = Kafka,kafka横空出世。
1) 一个装有干净的、结构化的、集成的数据repository,用于分析。
2) 虽然想法很美好,可是获取数据的方式有点过期了:周期性地从数据库获取数据,将其转换为某种可读性更佳的格式。
3) 以前的数据仓库问题在于:将干净的数据和数据仓库高度耦合。
数据仓库,应该是一组查询功能的集合,这些功能服务于报表、搜索、ad hot 分析,包含了计数(counting)、聚合(aggregation)、过滤(filtering)等操做,因此更应该是一个批处理系统。
可是将干净的数据和这样的一种批处理系统高度耦合在一块儿,意味着这些数据不能被实时系统消费,好比搜索引擎的索引构建、实时计算和实时监控系统,等等。
Jay哥认为,ETL无非作两件事:
1) 对数据进行抽取和清洗,将数据从特定的系统中解锁
2) 重构数据,使其能经过数据仓库进行查询。好比将数据类型变为适配某个关系型数据库的类型,将模式转换为星型或者雪花模式,或者将其分解为某种面向列的存储格式。
可是,将这两件事耦合在一块儿,问题很大,由于集成后的、干净的数据,本应能被其它实时系统、索引构建系统、低延时的处理系统消费。
数据仓库团队,负责收集和清洗数据,可是,这些数据的生产者每每由于不明确数据仓库团队的数据处理需求,致使输出很难被抽取和清洗的数据。
同时,由于核心业务团队对和公司的其它团队保持步调一致这件事儿不敏感,因此真正能处理的数据覆盖度很低,数据流很脆弱,很难快速应对变化。
因此,更好的方式是:
若是想在一个干净的数据集上作点搜索、实时监控趋势图、实时报警的事儿,以原有的数据仓库或者hadoop集群来做为基础设施,都是不合适的。更糟的是,ETL所构建的针对数据仓库的数据加载系统,对其它(实时)系统点儿用没有。
最好的模型,就是在数据发布者发布数据以前,就已经完成了数据的清洗过程,由于只有发布者最清楚它们的数据是什么样的。而全部在这个阶段所作的操做,都应该知足无损和可逆。
全部丰富语义、或添加值的实时转换,都应在原始的log发布后处理(post-processing),包括为事件数据创建会话,或者添加某些感兴趣的字段。原始的log依旧可被单独使用,可是此类实时应用也派生了新的参数化的log。
最后,只有对应于具体的目标系统的数据聚合操做,应做为数据装载的一部分,好比转换为星型或雪花型模式,以在数据仓库中进行分析和出报表。由于这个阶段,就像传统的ETL所作的那样,由于有了很是干净和规范的数据流,(有了log后)如今变得很是简单。
以log为核心的架构,还有个额外的好处,就是易于实现无耦合的、事件驱动的系统。
传统的 捕获用户活动和系统变化的方式,是将此类信息写入文本日志,而后抽取到数据仓库或者hadoop集群中进行聚合和处理,这个问题和前面所述的数据仓库和ETL问题相似:数据与数据仓库的高度耦合。
在Linkedin,其基于kafka构建了事件数据处理系统。为各类各样的action定义了成百上千种事件类型,从PV、用户对于广告的赶脚(ad impressions)、搜索,到服务的调用和应用的异常,等等。
为了体会上述事件驱动系统的好处,看一个简单的关于事件的例子:
在工做机会页面上,提供一个机会。这个页面应该只负责如何展现机会,而不该该过多地包含其它逻辑。可是,你会发现,在一个具备至关规模的网站中,作这件事,很容易就会让愈来愈多的与展现机会无关的逻辑牵扯进来。
好比,咱们但愿集成如下系统功能:
1) 咱们须要将数据发送到hadoop和数据仓库作离线处理。
2) 咱们须要统计页面浏览次数,以确保某些浏览不是为了抓取网页内容什么的。
3) 咱们须要聚合对此页面的浏览信息,在机会发布者的分析页面上呈现。
4) 咱们须要记录某用户对此页面的浏览记录,以确保咱们对此用户提供了有价值的、体验良好的任何适宜此用户的工做机会,而不是对此用户一遍又一遍地重复展现某个机会(想一想老婆不在家才能玩的游戏吧,那红绿蓝闪烁的特效,配合那劲爆的DJ风舞曲,或者那摇摆聚焦的事业峰和齐X小短裙的girls,而后点进去才发现是标题党的ad吧!)。
5) 咱们的推荐系统须要记录对此页面的浏览记录,以正确地追踪此工做机会的流行度。
很快,仅仅展现机会的页面逻辑,就会变得复杂。当咱们在移动端也增长了此机会的展现时,不得不把逻辑也迁移过去,这又加重了复杂程度。还没完,纠结的东西是,负责处理此页面的工程师,须要有其它系统的知识,以确保上述的那些功能能正确的集成在一块儿。
这只是个极其简单的例子,在实践中,状况只会更加复杂。
事件驱动可让这件事变得简单。
负责呈现机会的页面,只须要呈现机会并记录一些和呈现相关的因素,好比工做机会的相关属性,谁浏览了这个页面,以及其它的有用的与呈现相关的信息。页面不须要保持对其它系统的知识和了解,好比推荐系统、安全系统、机会发布者的分析系统,还有数据仓库,全部的这些系统只须要做为订阅者,订阅这个事件,而后独立地进行它们各自的处理便可,而呈现机会的页面不须要由于新的订阅者或消费者的加入而作出修改。
分离发布者和订阅者不新鲜,可是要保证多个订阅者可以实时处理消息,而且同时保证扩展能力,对于log系统来讲,是一件比较困难的事。
若是log的构建不具有快速、低开销和可扩展能力,那么创建在此log系统之上的一切美好都免谈。
不少人可能认为log系统在分布式系统中是个很慢、重型开销的活儿,而且仅用来处理一些相似于ZooKeeper更适合处理的元数据等信息。
可是Linkedin如今(注:2013年),在kafka中天天处理600亿条不一样的消息写入(若是算数据中心的镜像的话,那就是几千亿条写入)。
Jay哥他们怎么作到的呢?
1) 对log进行分割(partitioning the log)
2) 经过批量读写优化吞吐量
3) 避免没必要要的数据拷贝
经过将log切为多个partition来提供扩展能力:
1) 每一个partition都是有序的log,可是partitions之间没有全局的顺序。
2) 将消息写入哪一个partition彻底由写入者控制,经过依照某种类型的key(如user_id)进行分割。
3) 分割使得log的附加操做,能够不用在分片(sharding)之间进行协调就进行,同时,保证系统的吞吐量和kafka集群的规模呈线性关系。
4) 虽然没有提供全局顺序(实际上消费者或者订阅者成千上万,讨论它们的全局顺序通常没有啥价值),可是kafka提供了这样一种保证:发送者按照什么顺序将消息发给某个partition,从这个partition递交出去的消息就是什么顺序(什么顺序进,什么顺序出)。
5) 每一个partition都按照配置好的数目进行复制,若是一个leader节点挂了,其它的节点会成为新的leader。
6) 一条log,同文件系统同样,线性的读写模式可被优化,将小的读写log能够组成更大的、高吞吐量的操做。Kafka在这件事上作的很猛。批处理用在了各类场景之下,好比客户端将数据发送到服务端、将数据写入磁盘、服务器之间的数据复制、将数据传送给消费者,以及确认提交数据等场景。
7) 最后,kafka在内存log、磁盘log、网络中发送的log上,采用了很简单的二进制格式,以利于利用各类优化技术,好比零拷贝数据传输技术(zero-copy data transfer)。
诸多的优化技术,汇聚起来,可让你即便在内存爆满的情形下,也能按照磁盘或网络能提供的最大能力进行数据读写。
你觉得Jay哥提供了这么个美丽的方法把数据复制来复制去就完了?
你!错!了!
Log是流的另外一种说法,logs是流处理的核心。
Jay哥认为:
1)流处理是连续数据处理的基础设施。
2)流处理的计算模型,就如同MapReduce或其它分布式处理框架同样,只是须要保证低延迟。
3)批处理式的收集数据模式,致使了批处理式的数据处理模式。
4)连续的收集数据模式,致使了连续的数据处理模式。
5)Jay哥讲了个美国人口普查的方式来解释批处理。
在linkedin,不管是活动数据仍是数据库的变化,都是连续的。
批处理按天处理数据,和连续计算将窗口设为一天雷同。
因此,流处理是这样一种过程:
6)在处理数据时,带了一个时间的概念,不须要对数据保持一个静态的快照,因此能够在用户自定义的频率之下,输出结果,而没必要等数据集到达某种“结束”的状态。
7)从这个意义上讲,流处理是批处理的一种泛化,而且考虑到实时数据的流行程度,这是一种极其重要的泛化。
8)许多商业公司没法创建流处理引擎,每每由于没法创建流数据收集引擎。
9)流处理跨越了实时响应式服务和离线批处理的基础设施之间的鸿沟。
10)Log系统,解决了不少流处理模式中的关键问题,其中最大的一个问题就是如何在实时的多个订阅者模式下,提供可用数据的问题(流数据收集)。
流处理中最有趣的地方在于,其拓展了什么是数据源(feeds)这一律念。
不管是原始数据的logs、feeds,仍是事件、一行一行的数据记录,都来自应用程序的活动。
可是,流处理还可让咱们处理来自其它feeds的数据,这些数据和原始数据,在消费者看来,并没有二致,而这些派生的feeds能够包含任意程度的复杂性。
一个流处理任务,应该是这样的:从logs读取数据,将输出写入logs或者其它系统。
做为输入和输出的logs,连通这些处理自己,和其它的处理过程,构成了一个图。
事实上,以log为核心的系统,容许你将公司或机构中的数据捕获、转换以及数据流,看做是一系列的logs及在其上进行写入的处理过程的结合。
一个流处理程序,其实没必要很高大上:能够是一个处理过程或者一组处理过程,可是,为了便于管理处理所用的代码,能够提供一些额外的基础设施和支持。
引入logs有两个目的:
1) 保证了数据集能够支持多个订阅者模式,及有序。
2) 能够做为应用的缓冲区。这点很重要,在非同步的数据处理进程中,若是上游的生产者出数据的速度更快,消费者的速度跟不上,这种状况下,要么使处理进程阻塞,要么引入缓冲区,要么丢弃数据。
丢弃数据彷佛不是个好的选择,而阻塞处理进程,会使得全部的数据流的处理图谱中的处理进程卡住。而log,是一种很大,特大,很是大的缓冲区,它容许处理进程的重启,使得某个进程失败后,不影响流处理图谱中的其它进程。这对于一个庞大的机构去扩展数据流是很是关键的,由于不一样的团队有不一样的处理任务,显然不能由于某个任务发生错误,整个流处理进程都被卡住。
Storm和Samza就是这样的流处理引擎,而且都能用kafka或其它相似的系统做为它们的log系统。
(注:Jay哥至关猛,前有kafka,后有samza。)
不少流处理引擎是无状态的、一次一记录的形式,但不少用例都须要在流处理的某个大小的时间窗口内进行复杂的counts , aggregations和joins操做。
好比,点击流中,join用户信息。
那么,这种用例,就须要状态的支持。在处理数据的地方,须要维护某个数据的状态。
问题在于,如何在处理者可能挂掉的状况下保持正确的状态?
将状态维护在内存中多是最简单的,但抵不住crash。
若是仅在某个时间窗口内维护状态,当挂掉或者失败发生,那么处理能够直接回退到窗口的起点来重放,可是,若是这个窗口有1小时那么长,这可能行不通。
还有个简单的办法,就是把状态存在某个远程的存储系统或数据库中,可是这会损失数据的局部性并产生不少的网络间数据往返(network round-trip)。
回忆下,上文中曾提到的数据库中的表和log的对偶性。
一个流处理组件,可使用本地的存储或索引来维护状态:
经过记录关于本地索引的changelog,用于在crash后恢复状态。这种机制,其实也揭示了一种通常化的,能够存储为任意索引类型的,与输入流同时被分割(co-partitioned)的状态。
当处理进程崩溃,其能够从changelog中恢复索引,log充当了将本地状态转化为某种基于时间备份的增量记录的角色。
这种机制还提供了一种很优雅的能力:处理过程自己的状态也能够做为log被记录下来,显然,其它的处理过程能够订阅这个状态。
结合数据库中的log技术,针对数据集成这一场景,每每能够作出很强大的事:
将log从数据库中抽取出来,并在各类各样的流处理系统中进行索引,那么,与不一样的事件流进行join就成为可能。
显然,用log记录全时全量的状态变动信息,不太可能。
Kafka使用了log合并或者log垃圾回收技术:
1) 对于事件数据,kafka只保留一个时间窗口(可在时间上配置为几天,或者按空间来配置)
2) 对于keyed update,kafka采用压缩技术。此类log,能够用来在另外的系统中经过重放技术来重建源系统的状态。
若是保持全时全量的logs,随着时间增加,数据将会变得愈来愈大,重放的过程也会愈来愈长。
Kafka不是简单地丢弃老的日志信息,而是采用合并的方式,丢弃废弃的记录,好比,某个消息的主键最近被更新了。
Log,在分布式数据库的数据流系统和数据集成中所扮演的角色是一致的:
你能够将整个机构中的应用系统和数据流,看做是一个单独的分布式数据库。
将面向查询的独立系统,好比Redis , SOLR , Hive tables 等等,看做是一种特别的、数据之上的索引。
将Storm、Samza等流处理系统,看作一种精心设计过的触发器或者物化视图机制。
各式各样的数据系统,爆发性的出现,其实,这种复杂性早已存在。
在关系型数据库的辉煌时期(heyday),某个公司或者机构光关系型数据库就有不少种。
显然,不可能将全部的东西都丢进一个Hadoop集群中,指望其解决全部的问题。因此,如何构建一个好的系统,可能会像下面这样:
构建一个分布式系统,每一个组件都是一些很小的集群,每一个集群不必定能完整提供安全性、性能隔离、或者良好的扩展性,可是,每一个问题都能获得(专业地)解决。
Jay哥以为,之因此各式各样的系统爆发性地出现,就是由于要构建一个强大的分布式系统十分困难。而若是将用例限制到一些简单的,好比查询这样的场景下,每一个系统都有足够的能力去解决问题,可是要把这些系统整合起来,很难。
Jay哥以为在将来构建系统这事儿有三种可能:
1) 保持现状。这种状况下,数据集成依然是最头大的问题,因此一个外部的log系统就很重要(kafka!)
2) 出现一个强大的(如同辉煌时期的关系型数据库)能解决全部问题的系统,这彷佛有点不可能发生。
3) 新生代的系统大部分都开源,这揭示了第三种可能:数据基础设施可被离散为一组服务、以及面向应用的系统API,各种服务各司其事,每一个都不完整,却能专业滴解决专门的问题,其实经过现存的java技术栈就能看出端倪:
从某种角度来看,构建这样的分布式系统,就像某个版本的乐高积木同样。这显然跟更关心API的终端用户没有太大关系,可是这揭示了构建一个强大系统并保持简单性的一条道路:
显然,若是构建一个分布式系统的时间从几年降到几周,那么构建一个独立的庞大系统的复杂性就会消失,而这种状况的出现,必定是由于出现了更可靠、更灵活的“积木”。
若是一个系统,有了外部log系统的支持,那么每一个独立的系统就能够经过共享log来下降其自身的复杂性,Jay哥认为log的做用是:
1) 处理数据一致性问题。不管是当即一致性仍是最终一致性,均可以经过序列化对于节点的并发操做来达到。
2) 在节点间提供数据复制。
3) 提供“提交”的语义。好比,在你认为你的写操做不会丢失的状况下进行操做确认。
4) 提供外部系统可订阅的数据源(feeds)。
5) 当节点因失败而丢失数据时,提供恢复的能力,或者从新构建新的复制节点。
6) 处理节点间的负载均衡。
以上,大概是一个完整的分布式系统中应提供的大部分功能了(Jay哥确实爱Log!),剩下的就是客户端的API和诸如一些构建索引的事了,好比全文索引须要获取全部的partitions,而针对主键的查询,只须要在某个partition中获取数据。
(那把剩下的事情也交代下吧,Jay哥威武!)
系统可被分为两个逻辑组件(这强大的理解和功力):
1) Log层
2) 服务层
Log层,以序列化的、有序的方式捕获状态的变化,而服务层,则存储外部查询须要的索引,好比一个K-V存储可能须要B-tree、sstable索引,而一个搜索服务须要倒排索引。
写操做既能够直接入log层,也能够经过服务层作代理。写入log会产生一个逻辑上的时间戳(log的索引),好比一个数字ID,若是系统partition化了,那么,服务层和log层会拥有相同的partitions(但其各自的机器数可能不一样)。
服务层订阅到log层,而且以最快的速度、按log存储的顺序追log,将数据和状态变化同步进本身的本地索引中。
客户端将会获得read-your-write的语义:
经过对任一一个节点,在查询时携带其写入时的时间戳,服务层的节点收到此查询,经过和其本地索引比较时间戳,若是必要,为了防止返回过时的老数据,推迟请求的执行,直到此服务节点的索引同步跟上了时间戳。
服务层的节点,也许须要、也许不须要知道leader的概念。在不少简单的用例中,服务层可不构建leader节点,由于log就是事实的来源。
还有一个问题,如何处理节点失败后的恢复问题。能够这样作,在log中保留一个固定大小的时间窗口,同时对数据维护快照。也可让log保留数据的全量备份并使用log合并技术完成log自身的垃圾回收。这种方法,将服务层的众多复杂性移至log层,由于服务层是系统相关(system-specific)的,而log层确能够通用。
基于log系统,能够提供一组完备的、供开发使用的、可做为其它系统的ETL数据源、并供其它系统订阅的API。
Full Stack !:
显然,一个以log为核心的分布式系统,其自己当即成为了可对其它系统提供数据装载支持及数据流处理的角色。一样的,一个流处理系统,也能够同时消费多个数据流,并经过对这些数据流进行索引而后输出的另外一个系统,来对外提供服务。
基于log层和服务层来构建系统,使得查询相关的因素与系统的可用性、一致性等因素解耦。
也许不少人认为在log中维护数据的单独备份,特别是作全量数据拷贝太浪费、太奢侈,但事实并不是如此:
1) linkedin(注:2013年)的kafka生产集群维护了每数据中心75TB的数据,而应用集群须要的存储空间和存储条件(SSD+更多的内存)比kafka集群要高。
2) 全文搜索的索引,最好所有装入内存,而logs由于都是线性读写,因此能够利用廉价的大容量磁盘。
3) 由于kafka集群实际运做在多个订阅者的模式之下,多个系统消费数据,因此log集群的开销被摊还了。
4) 全部以上缘由,致使基于外部log系统(kafka或者相似系统)的开销变得很是小。
Jay哥在最后,不只厚道地留下了不少学术、工程上的有价值的论文和参考连接,还很谦逊地留下了这句话:
If you made it this far you know most of what I know about logs.
终。