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

来源:互联网 发布:水果作曲软件中文版 编辑:程序博客网 时间:2024/05/21 17:22
第二部分:数据集成
让我先来解释一下我所说的“数据集成”的含义,以及为什么它如此重要,然后再看它是如何与日志相关的。

数据集成是让组织的所有数据在其所有的存储系统和处理系统之间可用。

“数据集成”这个措辞不是那么通俗,但我找不到更好的词汇了。认可度更高的术语ETL(Extract、Transform、Load)只覆盖了数据集成的一个有限的部分——填充一个关系型数据仓库。但我所描述的大部分可以想象成涵盖实时系统和流处理的更一般化的ETL。

在对大数据概念的狂热追捧下,数据集成很少被提及,然而我相信,“让数据可用”这个朴素的问题是一个组织可能关注的最有价值的事情之一。

数据的有效使用遵从马斯洛需求层次理论。金字塔的基层涉及所有关联数据的抽取,并能够将这些数据在一个适用的处理环境(可以是个复杂的实时查询系统,或只是文本文件和python脚本)下集中起来。为了便于读取和处理,需要以一致的方式对这些数据进行建模。以一致的方式抽取数据的基本需求一旦被满足,那么在基础设施上以MapReduce,实时查询系统等不同方式对数据进行处理就变得合情合理了。

值得注意这个显而易见的事实:没有可靠完整的数据流,Hadoop集群只不过是非常昂贵和难以组装的加热器。一旦数据和处理过程可用,就可以将精力放到提炼后的,有着更好数据模型和一致的、语义清晰的问题上。最终,注意力可以转移到更加复杂的处理过程上——更佳的可视化、反馈(报告)、算法处理,及预测。

根据我的经验,大多数组织在这个金字塔的基层有着巨大的空洞——他们缺少可靠完整的数据流,却想直接跳到高级数据建模技术。这完全是本末倒置。

所以问题是,我们如何在组织的所有数据系统之上建立可靠的数据流?

数据集成:两个难题
两个趋势让数据集成更加困难。

事件数据
第一种趋势是事件数据的崛起。事件数据记录了发生的事件而不是事件本身。在Web系统里,这意味着用户活动日志,也包括机器级别的事件和统计数据,这些对于运行和监控一个数据中心来说都是必需的。由于通常都是写入应用的日志,人们倾向于把它叫做“日志数据”,但这混淆了带有方法的形式。这些数据位于现代Web系统的核心:毕竟,Google的财富产生于对点击和印象关联性管道的建设——那就是,事件。

这个东西不仅仅局限于互联网公司,只是互联网公司已经完全数字化了,因而他们易于做出转变(变化首先发生于互联网公司)。金融数据很久以来都是以事件为中心的。RFID将此类追踪添加到了物理对象上。随着传统行业的数字化进程,我认为这种趋势还将继续。

这种类型的事件数据记录的是发生了什么,且会比传统数据库的空间使用大好几个数量级。这让数据处理面临着巨大的挑战。

专用数据系统库的涌现
这第二种趋势发端于近五年来流行起来的、通常可以免费获取的专用数据库系统的涌现。专用系统为在线数据分析、搜索、在线存储、批量处理、图形分析等目的而存在。

更多不同类型的数据组合,以及让这些数据在更多的系统上可用导致了巨大的数据集成问题。

结构化日志的数据流
日志是处理系统间数据流的天然的数据结构,诀窍很简单:
将组织所有的数据集中在一个中心日志系统上,并提供实时订阅。

每个逻辑数据源都可以作为它自己的日志建模。数据源可以是记录事件(如点击、PV等)的应用,或接受修改的数据库表。每个订阅系统尽可能快的从这个日志上进行读操作,将新的记录变化应用到它们自己的存储当中,并更新它们在日志中的位置。订阅者可以是任何形式的数据系统——缓存、Hadoop、其他网段的其他数据库、搜索系统等。

比如,日志这个概念为所有订阅者可被测量的每个变化提供了逻辑时钟。这使得推论相互关联的、不同的订阅系统之间的状态变得大为简化,因为它们各自都有已经读到的“时间上的点”。

为了让它看起来具体,设想有一台数据库和一组缓存服务器。日志提供了同步所有系统间更新操作,及推论每个系统所处时间点的方法。假设我们写了一条记录,日志条目为X,然后需要从缓存中读出这个记录。如果我们想保证看不到脏数据,我们只需确保不从还没有复制到X日志条目的缓存中读取数据即可。

日志还起到了在数据生产者和数据消费者之间做异步缓冲的作用。这在很多情况下很重要,特别是有复数的订阅者以不同速率消费数据时。这意味着一个订阅系统可以宕机,或停机进行维护,在系统可用之后再追上来:订阅者控制着它消费数据的速度。像Hadoop或数据仓库这样集群系统可能只是每小时或每天才读一次,而实时查询系统可能每秒钟就需要进行一次读操作。 原始数据源和日志系统都不了解不同的数据落地系统,因此消费者系统可以添加到传输管道,或从中删除,而不会对管道产生影响。

特别重要的是:落地系统只知道日志,而不了解原始系统的任何细节。消费者系统不需要关心数据是来自RDBMS,新出现的键值对存储系统,或不产生于任何形式的实时查询系统。这似乎是个小问题,但实际上却至关重要。

我在这里使用“日志”代替“消息系统”,或“发布/订阅”,是因为它语义特殊,而且是在支持数据复制的应用实践中所需的更加贴切的描述。我发现“发布/订阅”除了非直接的进行消息寻址之外,没有更多的含义——如果比较两个承诺“发布/订阅”功能的消息系统,会发现它们承诺的完全是不同的东西,并且大多数模型都不适用于这个领域。可以将日志系统看作有着一致承诺和强烈次序语义的一种消息系统。在分布式系统中,这种通信模型以“原子广播”的名称出现(这很糟糕)。

值得强调的是,日志仍然只是基础工具。它不是控制数据流的终点:接下来是围绕着元数据、模式、兼容性,以及处理数据结构和演变的细节。除非有可靠的、常规的方式处理数据流运作,否则语义的细节就是次要的。

在LinkedIn
在LinkedIn从一个中心化的关系型数据库迁移至分布式系统的组合时,我观察到了数据集成问题的凸显。
目前我们的主要数据系统包括:
-搜索;
-社交图谱;
-Voldemort(键值存储);
-推荐引擎;
-OLAP查询引擎;
-Hadoop;
-Terradata
-Ingraphs(监控图形和韵律?服务)
这些都是在专业领域提供高级功能的专门的分布式系统。

在我加入之前,使用日志作为数据流这个想法就一直与LinkedIn为伴了。我们最早开发的一个基础组件是一个叫做databus的服务,它在我们早期的Oracle表之上提供了一个日志来缓存数据抽象,以此扩展对数据库变化的订阅,来满足社交图谱和索引搜索的需要。

让我讲点历史来说明背景信息。我自己是在2008年后我们上线了键值存储之后参与其中的。我之后的项目是让一个Hadoop应用跑起来,并把我们的部分推荐处理进程转移到上面去。由于没有这个领域的相关经验,我们很自然的为数据转移预估了几周的时间,并准备用剩下的时间实现复杂的预测算法。长途跋涉就这样开始了。

一开始的计划只是将数据从我们现存的Oracle数据仓库中剥离。但很快就发现从Oracle中快速导出数据是很折磨人的一件事。更糟的是,数据仓库的处理过程不适用于我们为Hadoop设计的批量处理生产过程——大部分处理是不可逆的,且与生成的报告具体相关。我们只能绕过数据仓库直接获取源数据库和日志文件。最终,我们实现了另一个传输管道用来将数据导入键值存储并生成结果。

毫不起眼的数据拷贝最终成了原本开发进程的决定性项。更糟的是,这些数据管道中的任何一个都可能在任何时间出现问题,Hadoop系统几乎没有发挥作用——在错误数据之上运行复杂算法只会产生更多的错误数据。

尽管我们已经以相当通用的方式来构建,但每个新的数据源都需要个性化配置来完成建立。这也被证实是很多错误和失效的根源。我们基于Hadoop实现的网站功能变得流行,同时也有很多对此感兴趣的工程师。每个用户都有一个他们想集成的系统列表,以及想获得的更长的数据源列表。

译注:这家伙是希腊神话中的人物,因为犯了错被罚将一块巨石背上山顶,但每次当他到达山顶后,石头都会滚回山脚。
暗指重复没有意义的劳动。

有些事在我面前渐渐变得清晰。

首先,我们构建的传输管道是非常有价值的,尽管它们显得有点杂乱。只是让数据在新的处理系统(Hadoop)中可用的这个进程就解锁了很多的可能性。新的运算变得可能,而在以前是难以在这些数据之上实现的。将原本隔离在专有系统中的许多数据碎片集中起来,催生了许多新产品和分析技术。

其次,可靠的数据加载需要数据传输管道的深度支持,这是很明确的。如果捕获到我们所需的所有数据结构,我们完全可以让Hadoop数据加载实现完全的自动化,因而新增数据源或处理模式变化就不需要增加人力——数据会神奇的出现在HDFS系统上,Hive中的表会为新的数据源自动生成合适的列。

第三,我们的数据覆盖率还是很低。如果看一下LinkedIn存放于Hadoop系统中的数据的总体百分比,它仍然是不完整的。考虑到使每个新的数据源运作起来所需的精力,达到完整数据覆盖率将不是一件容易的事。

我们一直以来为每个数据源推行、扩建个性化数据加载的方式,明显是不可行的。我们有一打的数据系统和数据仓库。把这些系统都连接起来会导致以如下所示的方式在每对系统间构建定制的传输管道:

注意到数据通常是双向流动的,因为许多系统(数据库、Hadoop)同时是数据传输的源头和终点。这意味着最终我们会为每个系统构建两条传输管道:一个用于数据输入,一个用于数据输出。

这显然需要一大批人,而且也不具备可操作性。随着我们接近全连接,最终我们将有着差不多O(N2)条传输管道。

作为替代,我们需要的是这样一个更通用的模型:

这些经历让我专注于构建Kafka系统来联合我们在带有日志概念的消息系统中看到的,这些概念在数据库和分布式系统内部非常流行。我们首先想要一个作为所有活动数据的中心传输管道的东西,逐渐扩展到许多其他用途,包括Hadoop之外的数据部署,监控数据等。(这行翻译的不好。。。)

有很长一段时间,Kafka是作为一个独特的(有人会认为是古怪的)底层产品存在——不论是数据库,或日志文件收集系统,还是传统的消息系统。最近亚马逊提供了一项跟Kafka非常相似服务Kinesis。这种相似性从处理分片的方式,数据的保持,以及在Kafka API中对高级和低级消费者相当古怪的分割上均有体现。我对此感到很高兴。这表明我们创建了一个好的底层工具抽象,而AWS将它作为一个服务提供出来!他们对它的展望跟我所描述的非常相似:是传输管道连接了他们所有的分布式系统——DynamoDB、RedShift、S3等,同时也是使用EC2进行分布式流处理的基础。

与ETL和数据仓库的关系
我们来讲一点数据仓库的东西。数据仓库代表着为了支持分析而结构化的干净、集成的数据容器。这是一个伟大的想法,对于那些不了解的人,数据仓库方法论关乎周期性的从数据源抽取数据,把它改为某种可以理解的形式,并将其加载到一个中心数据仓库。有这样一个中心地点来存储所有数据的干净拷贝,对于数据密集的分析和处理来说是有着巨大价值的东西(资产)。在高级层面,这个方法论不会有很多改变,不论你是用像Oracle这样的传统关系型数据仓库,还是Teradata或Hadoop,尽管可能会需要调整加载和修改的顺序。

包含干净、集成的数据的数据仓库是可感知的资产,但是得到它的过程却有点过时了。

以数据为中心的组织的核心问题是将干净、集成的数据耦合到数据仓库。数据仓库是一组非常适用于不同形式生成报告和分析的查询基础组件,尤其是当查询涉及简单技术,聚合和过滤的时候。但是只有一组系统作为仅有的干净、完整的数据的仓库,意味着对于要求实时订阅(?)的系统来说数据不可用——实时处理、搜索索引、监控系统等。

在我看来,ETL其实就做了两件事:第一,它是抽取和数据清理的进程——本质上解放了锁定在组织不同系统上的数据,并移除了系统相关的不合理处。第二,数据被重造以用于数据仓库查询(如,是指适用于关系型数据库的类型系统,强转到star或snowflake模式,又或者打散到一个高性能的列存储格式等)。合并这两件事是个问题。干净、集成的数据仓库应该随时可用,不论是对实时系统,还是低延迟处理进程,或者其他实时存储系统中的索引。

我想这对让数据仓库ETL更加有组织的伸缩有益处。数据仓库团队的经典问题是,他们有责任收集、清理组织中所有其他团队产生的数据。激励是不均等的:数据生产者通常不是很关心数据在数据仓库中的使用,因而最后会创建难以抽取,或需要繁杂的、不易分离的转换机制来完成向有用数据形式的转变。当然,核心团队总是不能相当的管理规模扩展来适应组织中其他部分的步伐,因此数据覆盖率参差不齐,数据流很脆弱,改变很缓慢。

好一点的办法是创建一个中心传输管道,使用日志和定义良好的API来添加数据。与这个传输管道集成并提供干净、组织良好的数据订阅源的责任,要依靠这个数据订阅源的生产者。这意味着作为他们系统的设计和实现的一部分,他们必须考虑将数据导出并转入更好的数据结构用来传输给中心传输管道。添加新的存储系统对数据仓库团队来说没有影响,因为他们有一个集成的中心点。数据集成团队只需处理更简单的问题:从中心日志上将结构化的订阅源数据导入,并执行针对于他们系统的转换。

当考虑在传统的数据仓库之外引入额外的数据系统时,有组织的可伸缩性这点就变得特别重要。比如说,希望提供基于组织的全部数据集的搜索能力。或希望提供有着趋势变化图形界面的对数据流的次秒级的监控能力。在其中任何一种情况下,传统数据仓库,甚至是Hadoop集群都将不再适用。更糟的是,对满足其他系统的需要而言,为了支撑数据加载而建的ETL处理管道很可能没用,让这些基础设置自力更生跟采用数据仓库一样是个大工程。这看起来不太可行,也解释了为什么大多数组织都不具备这些能力来处理他们的数据。相比之下,如果组织已经增建了一致的,良好格式的数据源,让任何新的系统获取对所有数据的访问只需要简单的到传输管道的集成。

这个架构还为特别的清理或转换能在何处进行提供了一组不同的可选项:
1、可以由数据生产者在将数据添加到公司范围日志之前完成;
2、可以作为对日志的实时转换来完成(反过来产生一个新的,转换后的日志);
3、可以作为加载数据到某个目的地数据系统的过程的一部分;

最好的模式是让清理在将数据发布到日志之前由数据发布者完成。这种方式保证了数据是合规的,并且不会留存产生数据的特殊代码或维护数据的存储系统中的保留格式。这些细节最好由产生数据的团队处理,因为他们最清楚自己的数据。任何应用月这个阶段的逻辑都应该是无损耗的和可逆的。

实时处理中可以完成的任何形式的增值转换都应当在对原始日志源产生流程之后完成。这将包括事件数据的会话,或添加普遍感兴趣的衍生字段。原始日志仍然可用,但是这个实时处理过程产生了包含增量数据的衍生日志。

最后,只有针对这个目的系统的聚合应该作为加载过程的一部分被执行。这可能包括在将数据转换至特别的star或snowflake模式,以用于在数据仓库中进行分析和生成报告。因为这个阶段(通常被自然的映射到传统的ETL过程),现在是在远端的清理者和统一的一套流之上完成,应该尽可能的简化。

日志文件和事件
我们来讲讲这个架构的一个额外好处:它使得解耦的、事件驱动的系统成为可能。

互联网行业中对活动数据的典型做法是将其记录到文本文件,从文件便可以导入数据仓库或Hadoop集群用于聚合或查询。这跟所有批量ETL模式有着同样的问题:它耦合了数据流和数据仓库的容量及处理计划。

在LinkedIn,我们使用日志作为中心的风格构建我们的事件数据处理。我们使用Kafka作为中心的、复数订阅者的事件日志。我们定义了几百个用于捕获特别类型动作的唯一属性的事件类型。涵盖了页面访问量,广告印象(?),搜索,以及服务调用和应用异常等。

为了理解这样做的好处,设想一个简单的事件——在工作信息页面展示一个工作内容的提交。工作信息页面应该只包括需要展示工作内容的逻辑。但是,在一个动态网站上,最终很容易造成添加与工作信息展示不相关的额外逻辑。比如,我们需要集成如下几个系统:
1、我们需要把数据发送到Hadoop和数据仓库,用于线下处理;
2、我们需要记录访问量来确保访问者不是在尝试某种内容擦除;
3、我们需要聚合访问量,用于在工作信息发布者的分析页面展示;
4、我们需要记录访问量确保我们为用户正确的做工作推荐(我们不想重复展示同样的内容);
5、我们的推荐系统需要记录访问量以正确的跟踪该工作的流行度;
6、其他;

很快,展示工作信息的简单行为就变得非常复杂。随着我们增加其他需要展示工作信息的地方——移动应用等,逻辑必须同样迁移,复杂度也越来越高。更糟的是,我们需要交互的系统变得纠缠不清——工作在UI部门的人需要了解很多其他系统和特性,来保证它们进行合适的集成。这只是问题的演示版,任何真实的应用都只可能更加复杂。

“事件驱动”样式提供了对此的简化方案。这个工作显示页面现在仅仅展示了一个工作,并且记录了和显示这个工作相关的一些属性,这些属性包括工作属性,viewer的属性和其他有用的信息属性。每个其它感兴趣的系统——推荐系统、安全系统、工作信息发布者分析系统,和数据仓库——都只是订阅数据源来进行他们的处理。展示代码不需要关心这些系统,当新的数据消费者被添加后也不需要做修改。

构建可伸缩的日志
当然,把发布者与订阅者分离不再是什么新鲜事了。但是如果你想要确保提交日志的行为就像多个订阅者实时的分类日志那样记录网站发生的每件事时,可扩展性就会成为你所面临的首要挑战。如果不能构建足够快速、廉价和可伸缩的日志系统让它实际的扩展,使用日志作为通用的集成机制不过是美好的幻想。

0 0