diff --git a/log-what-every-software-engineer-should-know-about-real-time-datas-unifying/part2.md b/log-what-every-software-engineer-should-know-about-real-time-datas-unifying/part2.md index fa6fd84..5da6bf9 100644 --- a/log-what-every-software-engineer-should-know-about-real-time-datas-unifying/part2.md +++ b/log-what-every-software-engineer-should-know-about-real-time-datas-unifying/part2.md @@ -36,9 +36,9 @@ ### 事件数据管道 第一个趋势是增长的事件数据(`event data`)。事件数据记录的是发生的事情,而不是已存在的事情。 -在`web`系统中,这就意味着用户活动日志,还有为了可靠地操作和监控数据中心机器的价值所记录的机器级别的事件和统计数字。 +在`Web`系统中,这就意味着用户活动日志,还有为了可靠地操作和监控数据中心机器的价值所记录的机器级别的事件和统计数字。 人们倾向称它们为『日志数据』,因为它们经常被写到应用日志中,但这样的做法混淆了形式与功能。 -这些数据是现代`web`的核心:归根结底,`Google`的财富来自于建立在点击和展示(`clicks and impressions`)上的相关性管道(`relevance pipeline`),而这些点击和展示就是事件。 +这些数据是现代`Web`的核心:归根结底,`Google`的财富来自于建立在点击和展示(`clicks and impressions`)上的相关性管道(`relevance pipeline`),而这些点击和展示就是事件。 这些事并不是仅限于网络公司,只是网络公司已经完全数字化,所以更容易记录。财务数据长久一直是以事件为中心的(`event-centric`)。 [`RFID`](http://en.wikipedia.org/wiki/RFID)(无线射频识别)赋予了物理对象这种跟踪能力。 @@ -237,7 +237,7 @@ -从上面讨论可以看出,当考虑采纳传统的数据仓库之外额外的数据系统时,组织级的伸缩性(`organizational scalability`)显得尤为重要 +从上面讨论可以看出,当考虑采纳传统的数据仓库之外额外的数据系统时,组织级的伸缩性(`organizational scalability`)显得尤为重要。 例如,想为组织的所有的数据集提供搜索能力。 或者想为数据流的监控的次级监控(`sub-second monitoring`)添加实时数据趋势和告警。 无论是哪个情况,传统的数据仓库的基础设施,甚至是`Hadoop`聚簇都将不再适合。 @@ -266,7 +266,7 @@ 最后,只有针对目标系统的聚合操作才应该加到加载过程中。 比如可能包括在数据仓库中为分析和报表而做的把数据转化成特定的星型或者雪花状模式。 因为在这个阶段(一般比较自然地对应到传统的`ETL`处理阶段),现在处理的是一组规整得多和统一得多的流, -处理过程已经大简化了。 +处理过程已经大大简化了。 日志文件与事件 --------------------- @@ -280,51 +280,76 @@ 在`LinkedIn`,我们以中心日志的风格构建事件数据处理。 我们使用`Kafka`做为中心的多订阅方的事件日志,定义数百种事件类型, 每种类型都会捕获一个特定动作类型的独特属性。 -这样的方式覆盖从页面浏览、广告展示、和搜索到服务调用、应用异常的方方面面。 +这样的方式覆盖从页面浏览、广告展示、搜索到服务调用、应用异常的方方面面。 -为了进一步理解这一优势:设想一个简单的事务 —— 在日志页面显示已发布的日志。这个日志页面应当只包括显示日志所需要的逻辑。然而,在相当多的动态站点中,日志页面常常变的添加了很多与显示日志无关的逻辑。例如,我们将对如下的系统进行集成: +为了进一步理解这一优势,设想一个简单的场景 —— 显示在任务页面提交的任务信息。 +任务页面应当只包括显示任务所需的逻辑。 +然而,在足够动态站点中,任务页面很容易就变成任务显示无关的额外逻辑成为点缀。 +例如,我们将对如下的系统进行集成: -- 需要把数据传送到Hadoop和数据仓库中用于离线数据处理。 -- 需要对视图进行统计,确保视图订阅者不会攻击一些内容片段。 -- 需要聚合这些视图,视图将用于作业发布者的分析页面显示。 -- 需要记录视图以确保我们为作业推荐的使用者提供了恰当的印象覆盖,我们不想一次次的重复同样的事情。 -- 推荐系统需要记录日志用于正确的跟踪作业的普及度。 -- 等等。 +- 发送数据到`Hadoop`和数据仓库中,做离线数据处理 +- 浏览计数,确保查看者不是一个内容爬虫 +- 聚合浏览信息,在任务提交者的分析页面显示 +- 记录浏览信息,确保给用户推荐系统输入的是有效的展示(不想展示重复同样内容给用户) +- 推荐系统可能需要记录浏览,以正确的跟踪任务的流行程度 +- 等等 -不久,简单的作业显示变得相当的复杂。我们增加了作业显示的其它终端 —— 移动终端应用等 —— 这些逻辑必须继续存在,复杂度不断的增加。更糟的是我们需要与之做接口交互的系统现在是错综复杂的 _ 在为显示日作业而工作的工程师们需要知晓多个其它系统和它们的特征,才可以确保它们被正确的集成了。这仅仅是问题的简单版本,真实的的应用系统只会更加的复杂。 +有不了多久,简单的任务显示变得相当的复杂。 +与此同时,还要增加任务显示的其它终端 —— 移动终端应用等等 —— +这样的逻辑必须继续实现,复杂程度被不断地提升。 +更糟的是,我们需要交互的系统是多方需求交织缠绕在一起的 —— +负责显示作业的工程师需要知道多个其它系统和功能,才可以确保集成的正确。 +这里仅是简单描述了问题,实际应用系统只会更加复杂。 -“事件驱动”的模式提供了一种简化这类问题的机制。作业显示页面现在只显示作业并记录与正在显示的作业,作业订阅者相关的其它属性,和其它与作业显示相关的其它有价值的属性。每个与此相关的其它系统诸如推荐系统、安全系统、作业推送分析系统和数据仓库,所有这些只是订阅种子文件,并进行它们的操作。显示代码并不需要关注其它的系统,也不需要因为增加了数据的消费者而相应的进行变更。 +『事件驱动』风格提供了简化这类问题的方案。 +任务显示页面现在只负责显示作业并记录显示任务的信息,如任务相关属性、页面浏览者及其它有价值的信息。 +其它所有关心这个信息的系统诸如推荐系统、安全系统、任务提交分析系统和数据仓库,只需订阅上面输出的数据进行各自的处理。 +显示代码并不需要关注其它的系统,也不需要因为增加了数据的消费者而做改变。 构建可伸缩的日志 -------------------------- -当然,把发布者与订阅者分离不再是什么新鲜事了。但是如果你想要确保提交日志的行为就像多个订阅者实时的分类日志那样记录网站发生的每件事时,可扩展性就会成为你所面临的首要挑战。如果我们不能创建快速、高性价比和可扩展性灵活的日志以满足实际的可扩展需求,把日志做为统一的集成机制不再是美好的想像, +当然,把发布者与订阅者分离不再是什么新鲜事了。 +但是如果要给一个需要按用户扩展的(`consumer-scale`)网站提供多个订阅者的实时提交日志, +那么可伸缩性就会成为你所面临的首要挑战。 +如果我们不能创建快速、低成本和可伸缩的日志以满足实际大规模的使用,把日志用作统一集成机制只不过是个美好的幻想。 -人们普遍认为分布式日志是缓慢的、重量经的概念(并且通常会把它仅仅与“原数据”类型的使用联系起来,对于这类使用Zookeeper可以适用)。但是深入实现并重点关注分类记录大规模的数据流,这种需求是不切实际的。在LinkedIn, 我们现在每天通过Kafka运行着超过600亿个不同的消息写入点(如果统计镜相与数据中心之间的写入,那么这个数字会是数千亿。) +人们普遍认为分布式日志是缓慢的、重量级的抽象(并且通常只把它与『元数据』类的使用方式联系在一起,`Zookeeper`可能才适用) +但有了一个专注于大数据流的深思熟虑的实现可以打破上面的想法。 +在`LinkedIn`,目前每天通过`Kafka`写入超过600亿条不同的消息。 +(如果算上[数据中心之间镜像](http://kafka.apache.org/documentation.html#datacenters)的消息,那么这个数字会是数千亿。) -我们在Kafk中使用了一些小技巧来支持这种可扩展性: +为了支持这样的规模,我们在`Kafk`中使用了一些小技巧: 1. 日志分片 -1. 通过批处理读出和写入优化吞吐力 -1. 规避无用的数据复制。 +1. 通过批处理读出和写入来优化吞吐量 +1. 规避无用的数据拷贝 为了确保水平可扩展性,我们把日志进行切片: ![](images/partitioned_log.png) -每个切片都是一篇有序的日志,但是各片之间没有全局的次序(这个有别于你可能包含在消息中的挂钟时间)。把消息分配到特定的日志片段这是由写入者控制的,大部分使用者会通过用户ID等键值来进行分片。分片可以把日志追加到不存在协作的片段之间,也可以使系统的吞吐量与Kafka聚簇大小成线性比例关系。 +每个切片的日志是有序的,但是分片之间没有全局的次序(这个有别于在你的消息中可能包含的挂钟时间)。 +由写入者决定消息发送到特定的日志片段,大部分使用者以某种键值(如用户`id`)来进行分片。 +追加日志时,分片方式在片段之间可以不需要协调,并且可以使系统的吞吐量与`Kafka`集群大小线性增长。 -每个分片都是通过可配置数量的复制品复制的,每个复制品都有分片的一份完全一致的拷贝。无论何时,它们中的任一个都可以做为主分片,如果主分片出错了,任何一个复制品都可以接管并做为主分片。 +每个分片通过可配置数字指定数据复制的复本个数,每个复本都有分片的完全一致一份拷贝。 +任何时候都有一个复本作为`leader`,如果`leader`出错了,会有一个复本接替成为`leader`。 -缺少跨分片的全局顺序是这个机制的局限性,但是我们不认为它是最主要的。事实上,与日志的交互主要来源于成百上千个不同的流程,以致于对于它们的行为排一个总体的顺序是没什么意义的。相反,我们可以确保的是我们提供的每个分片都是按顺序保留的。Kafka保证了追加到由单一发送者送出的特定分片会按照发送的顺序依次处理。 +缺少跨分片的全局顺序是个局限,但是我们没有发现它成为大问题。 +事实上,与日志的交互一般来源于成百上千个不同的处理流程,所以为所有处理提供全局顺序没有意义。 +转而需要的是,我们提供的每个分片有序的保证,和`Kafka`提供的 同一发送者发送给同一分区的消息以相同的顺序交付到接收者 的保证。 -日志,就像文件系统一样,是容易优化成线性可读可写的样式的。日志可以把小的读入和写出组合成大的、高吞吐量的操作。Kafka一直至立于实现这一优化目标。批处理可以发生在由客户端向服务器端发送数据、写入磁盘;在服务器各端之间复制;数据传递给消费者和确认提交数据等诸多环节。 +日志,和文件系统一样,对于顺序读写可以方便地优化。日志可以把小的读写合的大的高吞吐量的操作。 +`Kafka`非常积极做这方面的优化。客户端向服务器端的数据发送、磁盘写入、服务器之间复制、到消费者数据传递和数据提交确认 +都会做批处理。 -最终,Kafka使用简单的二进制形式维护内存日志,磁盘日志和网络数据传送。这使得我们可以使用包括“[0数据复制传送](https://www.ibm.com/developerworks/library/j-zerocopy)”在内的大量的优化机制。 +最后,`Kafka`使用简单的二进制格式维护内存日志、磁盘日志和传送网络数据。这使得我们可以使用包括『[0拷贝的数据传输](https://www.ibm.com/developerworks/library/j-zerocopy)』在内的大量的优化机制。 -这些优化的积累效应是你常常进行的写出和读入数据的操作可以在磁盘和网络上得到支持,甚至于维护内存以外的大量数据集。 +这些优化的积累效应是往往以磁盘和网络的速度在读写数据,即使维护的数据集大大超出内存大小。 -这些详细记述并不意味着这是关于Kafka的主要内容,那么我就不需要了解细节了。你可阅读到更多的关于LinkedIn的方法在这个[链接](http://sites.computer.org/debull/A12june/pipeline.pdf),和Kafka的设计总述在这个[链接](http://kafka.apache.org/documentation.html#design)。 +这些自卖自夸的介绍不意味着是关于`Kafka`的主要内容,我就不再深入细节。 +`LinkedIn`方案的更细节说明在[这儿](http://sites.computer.org/debull/A12june/pipeline.pdf),`Kafka`设计的详细说明在[这儿](http://kafka.apache.org/documentation.html#design),你可以读一下。 -----------------