如何理解Stream processing, Event sourcing, Reactive, CEP?(2)

上篇

  上一个章节我们谈论原始事件与事件汇总聚合分离的方式,导致了设计一个数据库专门写入原始事件,另外一个数据库对事件汇总聚合读取,这也是一种Event Sourcing事件溯源,现在以Twiiter的Tweets(类似微博)为案例再次解释说明一下:

最普通的Twitter设计是将用户发布的微博存储到关系数据库中,一个微博很简单:一些内容,时间戳和ID,用户只要点击"发布"这个按钮就会引起数据库的一个写操作。

Twitter的ES

 

另外一个方面,阅读者是从时间线读取Twitter数据库,如上图的Output(read)。

这两个方式的数据结构也是完全不同的,如下图:

twitter设计

阅读时,对于每条微博Tweet,不只是有微博的基本内容,而且有用户的名称,照片和发布信息,以及粉丝数量等等。

那么你如何从简单的输入转变到这种更加复杂的输出呢?当然,普通方式使用关系数据表设计一个数据表结构,然后将微博数据插入其中,再用下面SQL读取:

Twitter SQL

这是以时间先后查询最近100个Tweet,当然,更有甚者,会通过存储过程等语句提高性能,但是这种查询却无法伸缩扩展,而且给数据库带来非常大的负载。

当一个用户浏览他的时间线时,遍历他关注的那些人的Tweet也是很昂贵的,SQL查询是非常耗时的,开始,Twitter是提前计算用户的时间线,然后缓存结果,这样用户查看时会很快,为了这样做,他们需要一个处理过程来将适合写操作的单个事件翻译成适合读操作的汇总聚合,称为fanout service.

另外一个案例是Facebook,它有许多按钮比如like让你写些什么然后保存到Facebook的数据库中,当你点按Like时,就产生一个事件,数据结构很简单:用户ID以及所喜欢的条目ID。

如果从输出方面看,也就是从Facebook读取,这时会意想不到的复杂,不只是有喜欢的内容,还有作者名称和照片,然后显示有160216个人喜欢,有6027分享和12851评论,输入和输出数据结果如下:

facebook

这种输入到输出的翻译过程大概是这样:将简单的一个个事件作为输入,产生复杂的个性化的数据结构,你不能想象有什么数据库能够在一边更新一边输出这么多信息。也就是说,对于这样一个有100000个喜欢不断产生,而你要实时不断输出这些喜欢的输出内容,这种大量动态更新和大量读取同时操作的场景使用缓存和数据库都是很难实现的。

从以上Twitter和Facebook案例我们能发现一个重复出现的模式:输入事件,对应用户界面某个按钮,而且非常简单,不可变的,我们只是简单地存储它们,我们将它们看成是真相来源source of truth

从网站上看到的每个内容都是从这些原始事件读取,有一个处理流程专门从原始事件产生汇总结果,当新的事件不断进行,不断更新缓存,这个处理流程是确定的,可以重新启动。你可以将网站上发生的每件事都喂给这个流程,你甚至可以重构任何时刻的缓存,这是一种cached view of the event log.。

这种在真相来源于缓存的分离的美丽之处在于你的缓存,你能非规范化你的数据格式。所谓非规范化denormalize的意思是:在通常关系数据库中,如果有事情发生,你只能改变一个地方,规范化可以使得写操作快而简单,但是意味着你在读取时需要做更多工作,比如使用SQL的join进行联表查询。

为了加速读操作,你需要非规范化数据,在各个地方复制相同数据以便能够快速读取,问题是,如果原始数据变化了,所有复制的地方需要改变,对于普通数据库,这简直是噩梦。因为你也许可能不知道所有地方在哪里。如果你采取用一个专门流程来对原始事件进行构建,你就有很大自由进行非规范化,因为你知道数据流自哪里。

下面我们再以LinkedIn为例。

下篇

Twitter的分布式日志DistributedLog

替代传统事务的并发建议

微服务的最终一致性与事件流

日志是每个软件工程师关心的统一数据抽象

Reactive编程

Event Sourcing