存储 频道

郭斯杰:重新思考流计算时代的分布式存储

  导语:本文根据郭斯杰老师于第十届中国系统架构师大会(SACC 2018)的现场演讲《从文件存储,对象存储到流存储 - 重新思考流计算时代的分布式存储》内容整理而成。

  讲师介绍:

  郭斯杰,Streamlio的联合创始人之一。Streamlio是一家专注于构建下一代流计算基础设施的初创公司。他也是Apache BookKeeper的PMC主席,Apache Pulsar的PMC成员。在创立Streamlio之前,他是Twitter消息团队的技术负责人。

  演讲正文:

  大家好,我今天主要分享的是在流计算时代,关于分布式存储的一些思考。自我介绍一下,我是一个开源爱好者,我主要参与的两个开源社区,Pulsar,一个雅虎开源的下一代流数据平台;然后BookKeeper,一个分布式的日志存储系统。在这之前我在Hadoop也做了一些跟Hive、HBase相关的工作。

  我目前在硅谷的一家创业公司叫Streamlio,顾名思义,Streamlio其实是由三个单词构成的,就是Stream、ml加IO。所以意图很明显,我们主要是做跟流相关的基础架构,未来我们可能会在基于流的基础架构之上再去做一些ml相关的事情。和流相关的基础机构包括消息中间件、存储、还有计算。在加入Streamlio之前,我在Twitter待了五年,主要负责整个Twitter的消息中间件平台以及数据库复制,包括跨机房复制相关的基础设施。在Twitter之前的话,我是在雅虎做了三年多的时间,然后是华中科大毕业的,后来在中科院计算所读的研究生。

  一、背景介绍

  我的题目其实是关于“实时时代”,或者说“流计算时代”,对于分布式系统我们应该怎么去考量?我们先看一下,八九十年代,我们最开始存储数据无外乎两种办法,一个是用数据库,另外一种就是以文件的方式进行存储。当你想去存一些数据时,基本上考量的就是这两个事情。后来慢慢演化出了很多分布式数据库、分布式文件系统。随着互联网的到来,数据量越来越大,没办法用传统的技术、本地文件系统、或者是网络文件系统去存大量数据,没有办法在原来的Oracle里面去存数据。因此就演化出了所谓的“神一般的存在”,就是Hadoop。其实Hadoop的整个生态的话是起源于Google的GFS和MapReduce这套系统,基本上现在大家说大数据,开源社区就一个东西,Hadoop,基本上所有互联网公司都会有一套Hadoop的系统。

  整个Hadoop的生态,其实是为批量大数据而存在的。随着云时代的到来,像AWS开始把Hadoop放到云上去,作为产品去卖,然后google也会有各种类似的一系列产品,这其实就是为批量以及大数据产生的一套自己的生态,而这个生态的基础,基本上是围绕分布式文件系统去做的存储。

  这是批量时代。批时代的到来,很多其实是Google主导,它主要是搜索引擎的应用,对时延的要求性不是很高,可以15分钟跑一个任务,去全网抓一些网页进行索引。

  但是随着一些新兴企业的产生,包括比如说像Twitter、Facebook这些社交媒体,会有大量实时产生的数据,就包括用户发的推特,然后你投递的广告以及移动互联网的兴起,会有大量移动端的数据,这个数据的到来就产生了另外一个概念叫做“流”。所谓“流”,它的诞生基本上是因为,虽然我有Hadoop,但是我的时延通常是几分钟甚至小时以上的。

  这个是因为在批量时代,它整个计算模型就是一个批处理的模型,所有的数据采集都是通过一个批量的任务,比如说每5分钟、每15分钟写成一堆文件,拿着这个文件提交给计算引擎去处理。随着应用发生变化,这时候你需要处理的响应时间会变得更长。所以在这个情况下,诞生了Storm + Kafka这种特殊的模式。这种模式最开始变得流行,是由Twitter带起来的。因为Twitter整个的产品定位,相当于每个人都是往Twitter上发消息,然后每个人都从Twitter上接受消息,整个的消息的投递、发送都是实时的,整个Twitter的业务是围绕“实时”构建的。

  所以在这种情况下,诞生了一个所谓的——基本上大家在做流的时候,不可避免地会认识到的一个东西,叫Storm。因为Storm是一个流计算的框架,但是你的存储本质上还是文件,也就是说我的数据的采集还是以5分钟、15分钟进来。但它不能真正意义上发挥流计算的潜质,所以在这种情况下,你需要的是一个新型的面向流的存储系统。

  在那个情况下,基本上大家都是用消息中间件,因此也是在这样的背景下,Kafka与Storm很天然的一个整合,让整个系统变成了流计算的一个事实规范。所以在这种情况下,就衍生出了后面更多的一些流计算框架,比如说Flink,以及Hadoop演化出来的Spark,Spark再继续做Spark Streaming,最后整个计算生态就变得百花争艳。

  但是这种情况下,假如我有一个批处理的数据,还有一个流处理的数据,这时候我们会发现在整个公司内部的数据架构,其实变成了两个部分,两个相互隔离的数据孤岛。其中一个数据孤岛是流数据孤岛(Streaming Data Silo),就是说完全是为了Streaming Data去做的一套架构,另外一个数据孤岛是批量数据孤岛(Batch Data Silo),也就是为了批量计算构建的一套独立系统。Streaming Data的话,你通常可以看到你需要一个消息中间件,类似于Kafka或者ACTIVEMQ,然后在上面可以跑流计算的一个引擎。在Batch的话,基本上还HDFS或者其他的分布式文件系统;在云上的话,可能就是对象存储如S3,GCS等类似的对象存储,这上面你可能需要有MapReduce或者是一个类似Hive的数据仓库。但它其实是相互隔离的一个状态。

  这个隔离会带来一个问题,你的两个数据其实是相互分离的。分离带来的问题是,我需要学两套API,需要两套集群去存放数据,然后围绕这个生态圈,需要两套工具去管理这些数据。

  怎么去解决出现的情况,在Twitter最开始提出的一个架构叫Lambda,意思是说,我有两层,一层是批处理层,就跑我的全量数据,包括MapReduce、包括Spark,但是因为它是批处理,时延是相对比较大的。这时我为了快,再加一个加速层。加速层的是怎么工作的?我的所有数据是通过消息中心进来,基本上是以流的方式进到计算引擎,然后拿一个流处理的计算引擎去做处理,处理完了以后,产生的是一个增量的结果,然后把这两个结果做整合,提供给最终的用户。

  所以Lambda的架构,其实只是把这两个Data Silo在最终的结果端做了一个汇总,让用户看起来是一个统一体。但实际上,你最开始的数据进来,会被分流到两个不同的系统里面去,一个是所谓的消息中间件,一个是文件系统或者对象存储。你整个业务的代价、机房的开销、硬件设施的代价、维护人员代价,其实都是双倍的。所以在这种情况下,我们发现Lambda会变得越来越重,因为随着数据量变得越来越大,本质上是没法使用Lambda支撑更大的业务。

  所以在这种情况下,就有人提出了Kappa的概念。它的概念其实很简单,就是所有的数据都以日志的方式存进来,因为日志它其实是一个append-only的存储。存进来了以后,日志本身上它具有流的一个特性,然后所有的访问都是以追尾读的方式去读,所以它可以做到消息中间间的低时延。那就可以把所有数据存起来,当你需要回放历史数据时,还可以做相应的批处理。

  在计算引擎上,因为你的数据表征变成一份了,你的计算引擎、API就可以统一起来,所以这也是后来为什么那么多计算框架在做批、流一体。比如Flink既可以支持批,也可以支持流。其实都在做一个事情,就是要让API在处理批跟处理流上是统一的,只需要学一个计算框架、一套API,就能做原来的两件事情。

  Kappa这个架构是很好的一个想法,但要真正做到真正意义上的Kappa,你不能拿一个消息中心间去做一个Kappa。因为消息中间件基本上是为消息,就是你的尾端数据去做设计的,所以它基本上没有考虑存储方面的一些特性。所以我们认为在批、流一体的时代,当你需要去做批、流一体化的计算时,其实是需要一个真正意义上的流存储。

  流存储的意思就是,你的数据可以通过流的一个或者append-only log的方式去表征。你最新的数据被append到log里面以后,它其实是作为我们通常意义上说的流计算的尾端数据进行处理,但你的数据被append之后会慢慢累加,就会变成你的历史数据。当你需要去做全量计算,需要回溯到7天前、30天前这样一个处理历史数据的时候,你可以进行相应的回溯。所以我们认为Kappa是你做批、流一体的一个正确基础,但是我们需要为Kappa去做一个真正意义上的流存储。所以我们streamlio大部分创始团队都是来自于Twitter、雅虎原来做消息中间件跟流计算的,所以我们一直在琢磨一个事情,就是说我们怎么去做真正的流存储。

  二、关于Apache Pulsar

  接下来,主要分享我们正在做的一些工作,然后我会回过头来怎么看我们做的工作是怎么映射到现在针对于批、流一体的计算方式下的一个考量。

  我们先来看一个特点,什么叫流存储?流存储,它其实两个单词,一个stream,一个是storage。Stream代表了它的访问模式,就是在数据写入以后,我的消费者或者我的读者,是立马可见的,不需要等经过比如5分钟的批处理,等文件全部写完了以后,才能提交到执行引擎去处理。通常stream的一个接口,就是传统意义上的消费订阅模式,就是说我的数据生产到这个stream里面之后,我的订阅者、消费者就立马可见。

  Storage的意思是跟传统意义上storage类似,它本质上是一个分布式存储,它需要保证我能够可靠稳定地保存数据,不丢数据,而且无论我什么时候再去访问这个数据,我的架构上都有能力把这个数据给存下来。因为我们刚才一直在说的就是所谓的大数据,有大量的数据要算,所以这个东西必须是水平可扩展的,并且是高吞吐的。

  所以我们围绕一个开源的项目叫Apache Pulsar来做这个事情。Pulsar是什么呢?其实最简单的、最容易理解的是,我们通常来说这个事情的话,我们会把它放到消息队列里面,就跟大家知道的Kafka、ACTIVE MQ、RabbitMQ都是在一个space里面,但是它不同的地方是什么?就是说Pulsar在整个设计上,并不只是一个简简单单的消息队列,我们通常用一句话来概括Pulsar是什么,“Flexible Pub/Sub messaging backed by durable log/stream storage”,它其实有两层意思,就是说你是一个面向消息或者面向流的消息系统,但是这个消息系统可以提供低延时地消费你的数据的能力,但是你的底层是以一个这种持久化的append-only的日志或者流的存储,作为分布式存储的一个支撑。

  在解释这个事情之前,我大概介绍一下这个项目。这个项目是在2012年雅虎内部启动,然后经过了无数的迭代以后,在2016年9月把它贡献、开源出来。在开源不到一年之后,去年6月份,把它捐献给Apache软件基金会,今年的9月它正式成为优异项目。在雅虎的Github上我们做了22个releases,然后把Pulsar捐献给软件基金会之后不到一年的时间,我们做了9个发布,基本上是不到一个半月有一次发布,目前是相对比较活跃的一个项目。

  Pulsar与传统消息系统的不同

  那我说了那么多,它跟传统的消息系统不同的地方是什么?首先是因为是消息系统,那不可避免地要跟传统的消息系统做对比。它在应用层面上其实是做了一个数据消费模型的统一,既支持传统的队列,也支持高性能的流的处理。这是从API的角度来说。但我们其实更关注的是分布式存储,它跟传统的消息系统不一样的地方是,传统的消息系统其实是面对消息数据去做的,所以它其实没所谓存储的概念。但是在Pulsar里面,我们其实是把存储跟计算分离,但计算更多的是强调messaging的那一块。

  我们把这两个分离了以后,同时把传统消息中间件使用的一个物理分区的模型,变成了一个更多分布式系统用的分片模型,那这样的话可以打造出一个既有实时消息系统的streaming的一个API,然后你又能够有一层是可提供无限存储的一个存储系统。

  灵活统一的消息模型:队列+流

  我简单说一下队列加流的模型,基本上在流计算里面都不可避免要用消息中间件,那消息中间件无外乎就是生产者、消费者,然后中间加一个topic,或者是consumer group和subscription这样的概念。

  不一样的地方是,像Kafka这样的一个消息中间件,它更多的是面向流去做设计的。像传统的ACTIVE MQ以及阿里的RocketMQ,更多的是面向队列去做设计,这时候Pulsar其实是在这上面做了一个统一。统一的意思是,我的生产者都往一个地方去发消息,这个消息的载体叫topic,但是我允许在topic上有不同的订阅模式。所谓的订阅模式,它处理的场景是不一样的,可以是顺序消费的streaming的模式,可以是共享消费的队列模式。

  举三个简单的例子,第一个我们叫独占式订阅,就是对于一个topic的一个流,只有一个消费者去消费,那么所有的消费都是顺序的,这时候我可以做很高的吞吐。

  另外一种灾备式订阅,其实是独占式的一个延伸,相当于有两个消费者或多个消费者,都想消费这个topic,但是在某一个时间段只有一个活跃的消费者,这个消费者可以源源不断的去消费,其他人只是他的一个灾备。如果活跃的消费者down掉了以后,其他消费者就可以立马接管,保证整个的处理是相对特别快的。

  另外一个方式,更多的是在线业务、订单业务、通知系统里面会用的共享式订阅,就是说对同一个topic,我可能不需要不在乎序,这时候我可以加无限个消费者去扩展消费能力,这些消息以共享的方式分发给不同的消费者。

  这是Pulsar在所谓的API模型上做的一个统一,但这只是给大家一个简单的一个概念,最主要的地方是,它其实是把传统的消息系统跟传统的分布式文件系统或者分布式存储系统做了一个整合。

  存储与计算分离

  另外提出的一个概念其实很简单,就是存储跟计算分离。分离的意思是说我有两层,一层是存储层,可以无限扩容,存很多数据;有一层是所谓的消息层,就是传统意义上的Broker,我可以提供很多低时延的消息投递,还有读尾端的数据。

  分层以后带来的一个好处是,每一层都可以独立扩展,比如当我需要更大的存储的时候,我可以只加存储节点。当我需要更多的消费者,需要更多人去读这个数据的时候,只需要加我的更多Broker就行了。分层以后还有一个好处是,我的Broker变成了没有状态,没有状态了以后,做一些容错、扩容就会变得特别简单,不需要搬很多的数据。

  在存储层,其实是用了另外一个项目叫Apache BookKeeper。它本质上一个专门针对分布式日志的存储系统。它的定位很简单,就是一个强一致的系统,是一个刷盘落盘的持久化存储,能保证即使落盘,我也能做到低延时跟高吞吐。它的设计会在可用性上做很多的文章,保证一些读写高可用。给大家一个大概的概念,BK可以做什么?BK最开始的一个主要应用场景是HDFS NameNode,它做的是NameNode的HA,就是说NameNode会有一个added log,它其实是整个HDFS的灵魂,就是说你所有对HDFS的Metadata的修改,都会落到added log里面进去。BookKeeper最开始出现就是为了解决HDFS NameNode HA的问题,但后来慢慢就衍生成了一个通用的分布式日志存储系统。

  它最主要的两个应用场景,我们概括出来,一个的是数据库,另外一个就是消息或者是流。BookKeeper用在数据库,主要介绍两个,一个是Twitter内部有Key Value存储Manhattan,BookKeeper作为Manhattan的transaction log去实现Manhattan Key Value的强一致性。另外一个是,Salesforce拿Salesforce来做一个类似于Amazon Aurora的一个NewSQL的数据库。所以这就是BookKeeper在数据库的应用场景,我们可以注意到BookKeeper在这里面其实是整个数据库的transaction log,基本上就是append-only的工作负载。

  另外一个场景是消息,基本上Twitter跟雅虎都是拿BookKeeper作为整个消息平台的存储。消息基本上也是append-only的工作负载,我之所以一直强调append-only是因为,BookKeeper的整个设计,就是为了日志或者为流诞生的。

  分片存储

  这给大家做一个简单的介绍,Pulsar的底层用的是一个面向流和面向日志的一个分布式存储系统,Pulsar围绕BookKeeper去构建消息中间件,或者是我们叫流存储的一个概念的话,它在传统意义上的这种topic分区上做了一个概念,就是说我的分区不再是一个物理的分区,其实是一个逻辑上的分区。

  逻辑上的分区的意思是,我可以无穷无尽地往分区上追加数据、写数据。但是在底层,我会把逻辑上的分区切成不同的分片,分片会均匀打散,存储到底层的一个存储系统里面。基本上HDFS或者其他的分布式文件系统都是这样的一个思路,所以它本质上就是用分布式存储来做消息系统,或者是做流存储的一个思路。

  我们刚才说了分层跟分片的概念,为什么我们说分层分片特别重要,它其实解决了很多可用性、容错、包括运维过程中的一些问题。我举两个例子,一个是关于容错,一个是关于扩容。

  容错方面的话,因为它其实是分成两层了,有一层是Broker,另外一层是存储节点。Broker失效了以后,其实它会很简单,因为记得所有的数据其实存在存储节点上,Broker本身是没有任何状态没有存储任何数据的,它只拥有一个分区的所有权。所有权的意思是,我可以提供分区的一个写服务,当它失效的时候,我只需要把所有权从失效的Broker换到其他在线的一些Broker就可以了,你的整个的流量、你的消费者、消费的数据都可以转到新的Broker上继续处理,所以基本上,基本在毫秒级别就可以完成。

  BookKeeper的容错上,其实跟传统的分布式文件系统是类似的,因为是计算跟存储分离了以后,你的存储基本上是被Broker层给隐藏了,所有的生产者、消费者都是跟Broker打交道,所以当你一个存储节点挂掉的时候,你的应用端其实是没有任何感知的,而整个失效恢复都是由存储节点在后台进完成。

  举个最简单的例子(如下图),在Bookie 2上,就是第二个存储节点上,我的Segment 4挂了或者整个第二个节点挂掉了以后,我Segment 4的副本数据从3变成2,为了保证数据可用性,需要把副本的数量从2调回到3。这时候后台有一个自动恢复机制,从第三个存储节点跟第四个存储节点,把Segment 4的数据重新复制到第一个存储节点上,从而保证数据有副本。所以这个是Bookie的容错,它整个容错会使你的应用端变的相对更能容忍失效的发生。

  另外一个好处是,扩容会变得特别简单,相当于只需要追加新的存储节点。追加新的存储节点的话,所有的Broker就能发现存储节点被新加进来了,当我需要继续写新的数据的时候,我会开新的Segment,新的Segment就会自然而然地落到新的存储节点,整个扩容就会自动、平滑、均匀地扩散到整个集群里面。这就是分层架构带来的一些容错和运维上的好处。

  下面是一个示意图,传统的消息中间件基本上都是基于物理分区去做的,因为它其实不是为了所谓的流存储去做设计的,它的设计理念就是,我只需要保证我的数据存储在一段时间内,我的消息消费完了就可以了。物理分区带来的问题是,数据跟存储节点是强绑定的,对于一个分区,是没办法增长到比存储节点更大容量的,这时候其实不适合用来做流存储或者是长远的一个存储。

  另外一个问题是,如果要做容错恢复,或者说做扩容的时候,会涉及到一个问题,因为我的物理分区其实是跟存储节点强绑定,强绑定带来问题是,如果要做负载均衡,要容错,这时候需要把物理分区重新拷贝到新的存储节点上,整个拷贝的过程其实是一个特别消耗带宽的过程。

  但是Pulsar的做法不一样,它把物理分区变成了一个逻辑分区。逻辑分区以后,整个分区的容量不再受限于单台机器,而是整个集群,这样的话你能做到真正意义上的流存储。逻辑分区以后,其实是把存储跟计算相互分离,这时候你的失效的处理会变得相对快速,然后没有痛点,然后你在每一层都可以做独立的扩展。

  举一个最简单的例子或更形象的一个例子,大家可以看下面这两张图,就是说你分区模型的话,所有分区的数据只能落在某一个存储节点上,你只能在这个节点上无限增长,但是它会有个顶,到了那个顶了以后,没有办法继续保存你的数据了。但是这种逻辑分区和分片的模型,其实是把你的数据切成不同的小的粒度,均匀打散到整个存储空间里面进去。这时候你其实能做到真正意义上的流存储,因为可以无穷无尽地存数据流。

  为什么要引入流存储?

  存储其实都是有一定限度的,而且很多情况下的话,已经有了HDFS,我在云上已经有太多的对象存储,有太多的文件系统存储,这时候我这些系统已经跑得好好的了,为什么还要引入一个新的系统?这个时候我们其实在想,我们做流存储的真正目的,并不是要再造一个存储系统,而是说我们要在整个数据的抽象上能做到统一,通过流的方式既能表征我的实时数据,也能表征我的历史数据,那这个时候我们完全可以利用已有的云存储,或者更廉价的存储去存已经变老的一些数据。

  因为我们其实是一个分片模型,分片模型带来的好处是,能够很自然地知道我的数据什么时候变老,当数据变老的时候,可以很容易地把整个数据卸载到廉价存储里面。但是从应用端来看,整个的数据还是以流的接口、流的表征方式去表达。你在编写计算的时候,不用去区分,你需要访问的是到消息队列里面去算实时计算,还是到HDFS里面去算实时计算,只需要通过统一的一个API就可以做批、流一体的计算,所以这就是我们围绕Pulsar,围绕流的模型以及分片模型做的一个事情。

  因为我们觉得在批、流一体的计算时代,你的数据如果还是以不同的方式去表征的话,其实还是存在这种所谓的冗余、数据拷贝、数据重复,然后两个数据源之间不一致的问题,通过流的方式可以统一地表达相应的数据。

  三、例子-交互式查询

  做流存储有什么好处呢?举一个最简单的例子,就是我们在Pulsar上面做的另外一个事情叫交互式查询。有时候很多应用场景,不仅仅只是想查7天前或者1天前的数据,可能是想把我7天前的数据,再加上这1小时的数据一起查,这个时候业务系统很难办,因为你7天前的数据在HDFS里面,你这1小时的数据在你的Kafka或者是各种MQ里面,你没办法把两个数据串在一起查。

  但是通过流存储的方式,我们其实可以很容易做到这一点。因为你整个流表征的是,历史数据跟实时数据,其实在统一的一个数据抽象里面,你的数据只要进到流里面,就是可以查的。这种交互式查询,具有传统意义上,像Hive这种交互式SQL能带来的一个复杂性,就是我可以写很复杂的SQL的查询,然后我同时还具有streaming SQL的实时性,但它不能完全等同streaming circle,更多的是说我编写了一条SQL,我让它不断的在那跑,然后它吐结果。交互式查询更多的是说,我一旦有一个需求,需要查我的历史数据加实时数据的时候,这个时候叫交互式的查询。

  我们在Pulsar里面做的一个事情叫,我们没有再去造一个SQL的轮子,而是用Facebook的Presto做了一个深度的开发。为什么我们用Presto,因为它本质上是一个纯的SQL查询引擎,它不带自己的存储节点,但它有自己的一个runtime,所以你可以运行相应的一个SQL查询。而且它可以查询不同的数据源,就是说如果你的业务是在已经在HDFS,你不想动了,但是你新的业务跑在Pulsar上,然后你想把这两部分的数据进行一个join或者是关联查询,这时候Pulsar可以支持不同的数据源进行查询,这时候不需要你把所有的数据都导到Pulsar里面再进行一个查询。

  它的实现其实相对特别简单,就像我刚才说的,整个的数据它是一个流,流被切成了不同的分段,不同的分段其实存储在不同的存储节点上,即使最尾端的分段,其实只要数据写进来了,就可以读。所以当你做SQL查询的时候,你的访问不再是受限于分区的数量,就是说,不再是当只有五个分区时,并发度只能是五。因为整个的分区是个逻辑分区,你的分片可能是一百个分片,这时候如果一百个存储节点,甚至是一百个执行节点,并发度就可以是一百,整个SQL查询的执行效率就会特别高,因为它是以分片为粒度,而不再是以分区为粒度。

  所以就像上图展示的,它实现的是一个多对多的数据访问,这个数据访问是因为Pulsar本质上是一个分布式的存储系统,它是一个分片存储,所以你整个的Presto的worker可以并发访问同一个分区的不同分片。我们其实在整个流里面第一次去加了时间索引,根据你的写入时间,就PublishTime快速定位分片,我们可以根据PublishTime快速做定位,然后我们就能知道要读哪些分片,不需要进行全扫描。

  四、总结

  总结一下我的分享,我们发现,在计算趋于批、流一体化的这种大浪潮下,所有的Spark、Flink、Beam……的API其实都是批、流一体。在这个时候,我们发现你的数据表征其实还是分散的,就是说你的实时数据、流数据还是以消息的方式去表征。你的历史数据是以文件系统或者是对象存储去表征,但是你会发现这部分数据它其实是同一部分数据。就是说你的消息跟你最后的文件系统,它们其实表彰的是同一部分数据。这个数据它只是一枚硬币的两面,你的流计算,是处理硬币的一面,然后你的批计算是处理硬币的另外一面。其实本质上在数据表征态的话,它不应该分开。流是原始数据最自然的一种表征,因为你所有的数据流进来,会不断的追加到流里面,其实不需要进行任何的一个转换,所以流是能够很自然地把所有的实时数据跟历史数据都保存下来。我们认为在实时时代,你需要有一个流存储作为数据的分布式存储,然后去实现真正意义上的批、流的一体化。

  以上是我今天的分享,当然可能大家会有不认同流存储的概念,欢迎提意见。

2
相关文章