大数据做了这许多年,有没有问过自己,大数据中,工作量最大和技术难度最高的,分别是什么呢?
01
大数据时代
正如下面的一句话:
我们从出生开始如果没思考过人生本身这件事情,一切按照社会的习惯前行,那人生是没有意义的。因为你连人生都没有想过。
那么延生出来,我们有没有想过大数据本身? 大数据到底是在做什么,为什么我做了这么多年的大数据,总是做不完呢?
大数据本质是:
随着科学技术发展,更多的数据能够被存储了,能被分析了。所以有了大数据的概念。
机器学习的本质是:
随着数据变多了,量变导致质变,数据足够大后其内部的隐含的规律会越来越精确和完整。机器学习则是将数据内存存在的这种隐含关联给挖掘出来的一项技术。
大数据最耗能工作量的地方是在哪里呢?
目前百分之八十的工作量都在于数据收集 清理和校验。 这个工作本身并不难,但是真的很繁琐,很费力。
我们天天感叹:
而让我们心灰意冷的是
当一个新的需求来临时,现有的数据形态似乎不能满足需求,我们又要在现有的数据堆里,重新走数据收集,清理,校验的流程。
这似乎是一种诅咒,如同可怜的西西弗斯,被判要将大石推上陡峭的高山,每次用尽全力, 大石快要到顶时,石头就会从其手中滑脱,又得重新推回去,幹著无止境的劳动。
大数据目前遇到的最大技术难点是什么?
是海量数据的ad-hoc查询
当Hadoop刚刚兴起,我们可以通过它来操控越来越廉价的PC服务器价格,于是一种暴力弥漫了整个生态:
我们因为突然有了强大的算力,这就好比一个穷人突然有了一笔很大的钱。我们开始让强大的算力驾着最低效的程序去跑数据,这是批处理时代的悲哀
但是随着查询效率要求越来越高,我们不得不被迫做出改变。还记得我们以前的日志都是简单的Raw文本吗? 现在各种存储的格式慢慢开花结果:
总之,我们似乎没有找到一个奇妙的技术解决查询的问题,只能做某种折中:
为了加快查询速度,数据存储慢慢从早期的raw文本转为具备向量化,带索引,支持特定编码和压缩的列式存储结构,当然这种通过调整存储结构的方式必然以消耗数据进入时的时间和资源为代价。
也就是我们在存储和查询之间做了妥协。
如何让苦力干的更少
前面我们提及了,我们可能80%的工作都花在了数据的采集,清洗和校验上了。但是我们该如何压缩这部分的工作呢?
答案是:
让所有的计算流动起来,就会让下面的事情变得简单:
我们可以在已经流动的数据中的任何一个环节引入一个新的支流。当我要获取数据时,我做的本质其实就是 连接两个或者多个节点,并且在其中对数据进行转换。就如同河水,我们可以很方便的开一个支流,将水引入灌溉新的额农田。
而且我们希望流式计算的实现是结合了流式和批量语义的。为什么呢?
看看华为在Storm上做的StreamCQL,就知道,很多情况实时流式是很有局限的,因为未来我们在流式上能做的事情会非常多:
这就需要一定的灵活性,因为只有在数据集上,才会有譬如Ad-Hoc查询,才能高效的进行存储,才能适应一些机器学习算法。单条数据很多情况下,是没有太大意义的。
这块我一直是Spark Streaming的支持者。数据天生就是流式的
那为啥我们需要一个流式计算上层建筑? 我们回顾下问题,数据的ETL过程是个苦力活,消耗掉大量程序员的工作时间,那么为了减少这种时间,我们有两个办法:
流式计算构建了整个基础,而其上的框架则使得上面两点成为可能。这里我依然推荐我现在正在做的一个开源项目: StreamingPro (https://github.com/allwefantasy/streamingpro)。未来我们还会有一个更通用的基于流式计算的采集程序。
02
流式数据
完全由流式计算构建的体系
部门目前核心其实就是流式计算,从根部开始(一个超大的Kafka集群)开始,延伸出一个超级庞大的树形结构。整个过程都是数据自我驱动进行流转,没有使用类似Azkaban/Oozie 等外部工具去让数据从一个系统流转到另外一个系统。 而我之前提出 Transformer架构 (http://www.jianshu.com/p/8a88a8bb4700)本质就是一个流式数据架构。
这个架构的核心概念是:
你开发的任何一个应用,本质上都是将两个或者多个节点连接起来,从而使得数据可以在不同节点之间流转
数据的流转必然由批量到流式
如果说在大数据领域,批量处理是第一次数据革命,那么流式处理则必然是第二次数据革命。
从某种角度而言,批量是流式处理的一个特例,譬如隔天处理数据,本质就是时间窗口为一天的流式计算。当然我们也可以实现以数量为窗口的计算。
当你需要借助外力的时候,事情往往就变得并不美好了。你需要额外的维护譬如Oozie等系统里的工作流,并且你需要考虑各个系统能够完成的时间,从而协调好组件。
数据流转的理想状态应该就如同河水一样,当源头水量变大后,水压会自动迫使数据流转速度加快。当我们需要灌溉新的农田时,我们只要接上一个蓄水池(比如Kafka,)在蓄水池延伸出新的河道(由流式引擎比如Spark Streaming完成),就可以很方便的将水引入。整个过程是水压驱动水的流转。
假设我们有河道A, 蓄水池C,河道B。水流方向是 A -> C ->B。 A 内部是典型的依赖于重力的将水压力蓄水池C。 而B 则因为地势可能更高些,需要靠消费额外的资源(CPU资源)将水抽取到B自己的河道里(pull 模式)。 当然,B也可能是地势低,这样C可以利用重力将水引入C (典型的push模式)。
批量与流式的微妙关系
批处理和流式本来就存在某种微妙的关系,我中有你,你中有我。Spark Streaming则充分利用了这种微妙关系,将其发挥到极致。批量处理是Spark Streaming流式处理的一个窗口特别大的特例,但是如果细加观察,Spark Streaming 的每个batch 又都是一个批处理,只是因为这个批处理可以足够小,看起来就像数据在真实流动一样,所以我们也称之为流式处理。
这里有个值得提出的东西是,当处理时间等于调度周期,那么spark streaming就是一个永不干涸的河道。而如果处理时间大于调度周期,则有两种情况需要阐述:
如果抽水泵不限制功率也不推延工作时间(Receiver模式容易出现),那么就让河道溢出了(OOM)了。
从某种角度而言,Spark Streaming 这种将批处理和流处理巧妙融合的方式可以保证自己可以充分利用流式和批处理的优势。
Storm这种流式引擎则能实现最细粒度的流转,但是这种细粒度的流转在很多场景并不足够高效,因为在流转的过程中,往往下游无法接受来一条就处理一条的情况,需要通过小窗口的batch来完成更加高效的入库操作。而获取数据,Storm从某种角度而言也是批处理。因为消费者每次从kafka 抽取数据的时候,也是一次抽取到足够的量,然后交给后端一条一条处理。
所以Storm 和Spark Streaming的本质区别在于抽水泵的工作机制。
几句话
总结
从宏观角度而言,批处理pipeline 一般而言借住一个协调组件,又该协调组件产生动力,调用各个系统完成某种功能。通常而言,批处理pipeline的数据处理周期都较长,符合离线的定义,譬如隔天,并且各个系统作为管道,只有在需要的时候才会被创建。
流式处理pipeline 则不需要借助外部协调组件,每个系统通过主动拉取或者推送的方式,完成数据在不同系统中的流转。通常而言,流式pipeline的数据处理周期都很短,符合准实时的定义,并且各个系统作为管道,都是一直存在的。