审校:李浩、宇亭
设计:Yeekin
责编:宇亭
导语
本篇是StoneDB学术分享会专栏的第八篇,在上一期里,我们分享了SAP 发表的《Efficient Transaction Processing in SAP HANA Database – The End of a Column Store Myth》,主要介绍了 SAP HANA 数据库如何通过列式存储实现同时在分析型和事务型工作负载环境中进行高效工作,从而号召大家终结对列式存储的偏见。
有心的同学可能会注意到,我们接连三期分享了 SAP 发布的论文,都是讲SAP 如何实现 HTAP 系统架构的,而且,似乎SAP 非常想向大家证明:我一个数据库是可以做到既能处理事务(OLTP)又能处理分析(OLAP)的,而且性能还挺好,几乎完美。
不过,这好像就是在说:咱们 SAP 的 HANA 是可以做到 “One Size Fits All” 的!看起来是和咱们今天的标题宣战呢~
没错,SAP 宣战的不是别人,正是数据库领域的祖师爷级别人物 Michael Stonebraker 先生(顺带一提,我们之所以叫 StoneDB 其实有一层含义就是为了致敬 Stonebraker 先生),在 2005 年的 ICDE 上,Stonebraker 发表了一篇名为《“One Size Fits All”: An Idea Whose Time Has Come and Gone》的文章,正式向学术界和工业界宣布:“One Size Fits All” 的理论过时了哈,一种类型一个数据库,才是常态,你看看,我OLAP搞了一个,OLTP搞了一个,内存数据库也搞了一个,总之,你得分门别类地每样搞一个!(这样大概就会很好卖)
后面的事情,熟悉数据库圈子的也都知道了,SAP 在 4 年之后的 SIGMOD 2009 年上当着 Stonebraker 的面介绍了他们研发出的的“One Size Fits All”—— SAP HANA,可以说是当众打了大佬的脸。
但是,大佬就不可以被打脸么?我倒觉得 SAP 还是挺有勇气的,如果没有像 SAP 这样的后起之秀扛起大旗来发出自己的声音,或许,十几年后的今天,数据库圈子都不敢再谈 HTAP 了。抛去商业界的竞争较量,我们回归到学术领域,仔细阅读 Stonebraker 先生的这篇文章,其实还是非常有理有据的,从狭隘的商业视角看,“One Size Cannot fit All”可能是为了某种商业营销,不过,从宏观的技术视角看,也正因为有了这个理论,我们如今的数据库产品种类才能如此繁荣多样。当然,此篇论文的观点仅供参考,读者应该有自己的判断,今天,就让我们来学习一下这篇经典论文吧~
摘要
过去25年间的商业数据库管理系统(database management system,DBMS)的发展可以用一个词来概括:“One Size Fits All”。“One Size Fits All”阐述了这样一个事实:传统的 DBMS 架构最初是为处理业务数据而设计和优化的,但如今却被用来支持许多以数据为中心的应用,这些应用在特征和要求上存在着巨大的差异。本文论证了“One Size Fits All”的策略不再适用于数据库市场。 数据库市场将被一系列的独立的数据库引擎切分成无数个细分市场。当然,在这些数据库引擎中,也许有一部分可以通过一个通用的前端解析器统一起来。
本文使用流处理市场和数据仓库市场作为例证来支持我们的观点。在本文中,我们还简要讨论了传统架构在其他市场中存在的水土不服的情况,并指出应该对将系统服务考虑进产品中的做法进行批判性思考。
关系型 DBMS 在20世纪70年代以 System R【10】和INGRES【27】作为产品原型出现。这两个原型的定位是在应用(即“业务数据处理”)上超越 IMS 对客户的价值。因此,这两个系统都是为联机事务处理(online transaction processing,OLTP)应用而设计的,它们的商业化产品 DB2 和 INGRES 在20世纪80年代得到了市场的认可。Sybase、Oracle、Informix 以及其他供应商也遵循了相同的基础 DBMS 模型。该模型逐行存储关系表,使用 B-tree 进行索引,使用基于成本的优化器,并提供 ACID 事务属性。
自20世纪80年代初以来,主要的 DBMS 供应商一直坚持“One Size Fits All”的策略,即所有的 DBMS 服务使用同一行代码。选择这种策略的原因很简单――使用多行代码会导致各种实际问题,包括:
- 成本问题:维护成本会随着代码行数线性增长,甚至增长得更快。
- 兼容性问题:所有的应用都必须能够支持每一行代码。
- 销售问题:销售人员不知道该向顾客推销哪种产品。
- 营销问题:如果使用多行代码,那么每一行代码必须有清晰、准确的市场定位。
为了避免这些问题,所有的主流 DBMS 供应商都遵循集中精力做一款产品的行业策略。本文我们主要论证这种策略在当下的数据库市场中早已行不通了,并且使用这种策略的缺点会逐渐全面地暴露出来。
本文的框架结构如下:第2章通过引用数据仓库市场的一些关键特征简要说明为什么一行代码闯天下的策略已经行不通了。第3章讨论流处理应用,并用一个案例来说明了流处理引擎比关系型 DBMS 在性能上高两个数量级。承接第3章,第4章讨论了造成这种性能差异的原因,并指出了传统 DBMS 技术缺乏市场竞争力这一事实。基于此,我们判断流处理引擎将成为 DBMS 市场发展的趋势。第5章讨论了一系列“One Size Fits All”策略可能不适用的市场。这些市场需要专门的数据库系统。DBMS 市场可能会被细化切分。第6章提供了一些我们关于将系统软件分解成产品的看法。最后,在第7章我们对我们的观点进行了总结。
在20世纪90年代初,DBMS 市场出现了一种新趋势: 出于 BI(商业智能,Business Intelligence)考虑,企业希望将来自多个操作型数据库的数据收集到一个数据仓库中。一个典型的大型企业约有50个操作型系统,每个系统都有一个联机用户(on-line user)社区,这些用户都期望快速响应。因此,系统管理员一直以来都不愿意让 BI 用户和联机用户使用相同的系统,担心 BI 用户执行的复杂即席查询会影响系统性能,造成系统响应时间过长。此外,BI 用户的常见需求还包括查看历史趋势以及关联多个操作型数据库的数据。在功能需求上,BI 用户与联机用户之间存在巨大的差异。
因此,基本上每个企业都创建了一个大型数据仓库,用于存储定期从操作型系统中同步过来的数据。BI 用户可以对仓库中的数据执行复杂的即席查询,而不会影响到联机用户。尽管大多数数据仓库项目都大大超出了预算,且最终交付的功能和供应商承诺交付的功能相比打了折扣,但是这些项目仍然带来了合理的投资回报。事实上,人们普遍承认存放零售交易历史数据的仓库在一年内就收回了成本。这主要是因为仓库帮助他们做出了更明智的存货周转和购买决策。例如,某 BI 用户可以通过仓库数据分析发现宠物石已经过时了,取而代之的是芭比娃娃的风靡,然后做出恰当的商品放置和购买决策。
因此,基本上每个企业都创建了一个大型数据仓库,用于存储定期从操作型系统中同步过来的数据。BI 用户可以对仓库中的数据执行复杂的即席查询,而不会影响到联机用户。尽管大多数数据仓库项目都大大超出了预算,且最终交付的功能和供应商承诺交付的功能相比打了折扣,但是这些项目仍然带来了合理的投资回报。事实上,人们普遍承认存放零售交易历史数据的仓库在一年内就收回了成本。这主要是因为仓库帮助他们做出了更明智的存货周转和购买决策。例如,某 BI 用户可以通过仓库数据分析发现宠物石已经过时了,取而代之的是芭比娃娃的风靡,然后做出恰当的商品放置和购买决策。
数据仓库模式中的标准做法是创建一个事实表,表中包含每个操作型事务的“人物、时间、地点、发生了什么”。图1描绘了零售行业中的典型数据仓库模式。请注意,中央事实表(central fact table)为连锁店中每个商店的收银员扫描的每个商品保存一条记录。数据仓库还包含维度表(dimension table)。这些维度表中记录了每个商店、客户、产品和时间段的信息。这种组成方式产生的效果是,事实表包含了每个维度的外键(foreign key),从而产生了星形模式。这种星型模式在数据仓库中随处可见,但在 OLTP 系统中却鲜有存在。
众所周知,仓库应用使用位图索引可以运行得更好,而 OLTP 用户则更倾向于使用 B-tree 索引。产生这种差异的原因很简单:位图索引在仓库工作负载上更快、更紧凑,但在 OLTP 环境中则收效甚微。 因此,许多供应商在其 DBMS 产品中同时支持 B-tree 索引和位图索引。
另外,物化视图在仓库环境中是非常有效的优化策略,但在 OLTP 环境中却毫无作用。OLTP 环境更适合使用普通视图(也称为虚拟视图)。
概括来讲,大多数供应商都有一个仓库型的 DBMS 和一个 OLTP 型的 DBMS。仓库型的 DBMS 使用位图索引、物化视图、星型模式以及针对星型模式查询的优化策略,OLTP 型的 DBMS 则 B-tree 索的引和一个标准的基于成本的优化器。二者由一个通用的解析器连接起来,如图2所示。
请先看图2中的例子。此例中,两个系统共用同一个用户界面(user interface)。这就给大家造成了一种假象:“One Size Fits All”具有可行性。接下来我们看看在流处理市场中,多个系统共用同一前端的想法是多么的不切实际。 在这个市场中,不仅会有不同的引擎,而且会有不同的前端。“One Size Fits All”的可行性假象将会在这个市场中被无情的戳破。
最近,研究界兴起了流处理应用的兴趣【7、13、14、20】。这种兴趣的来源于市场对未来几年内传感器网络的商业可行性的预期。虽然射频识别技术(Radio Frequency Identification,RFID)最近受到了所有媒体的关注,并将在零售行业中的供应链优化应用中得到广泛使用,但还有许多其他技术(例如 Lojack【3】)值得我们关注。大量低成本传感器设备的使用给人们的生活带来了翻天覆地的变化,也衍生出了监控应用这个新兴市场。许多行业专家将眼光投向了这片蓝海。
#3.1 传感器应用的兴起
传感器网络技术在军事领域有着广泛应用。例如,美国陆军正在研究在所有士兵身上安装生命体征监视器,从而可以实现在战时优化医疗分类。许多军用车辆中已经安装了 GPS 系统,但这些 GPS 系统之间尚未连接形成一个闭环。而陆军希望能够监控到所有车辆的位置,实时识别出哪些车辆偏离了路线。在炮塔上安装传感器也列入了考虑范围。这样再加上位置信息,陆军就能检测到交火情况。另外,如果在汽油表上安装传感器,就可以实现优化加油。总之,一个由30,000人和12,000辆车组成的军营很快将会被一个由几十万个节点组成的大规模传感器网络所覆盖。这个网络需要实时传送车辆状态和位置信息。
这就要求网络中的处理节点和下游服务器必须能够处理这种 “流水” 数据。必需操作包括复杂的警报(例如,“排里的4辆车,什么时候有三辆车进入到前线?”)、历史查询(例如,“在过去的两个小时内,编号12的车辆去了哪里?”)、以及纵向查询(例如,“部队现在的总体准备状态是什么?”)。
随着时间的推移,基于传感器的监控应用会逐渐出现在非军事领域中,比如交通拥堵监控以及旅游路线推荐。还有一个和军事领域相似的应用:可变的、基于拥堵的高速公路系统收费系统。这个应用的灵感来源于《线性道路基准(Linear Road Benchmark)》【9】。游乐园将很快使用传感器来取代顾客使用的普通腕带,这样不仅可以优化游乐设施还可以定位走丢的小朋友。手机早已活跃于人们日常生活中。我们可以幻想有这么一个服务可以为饥肠辘辘的用户定位到最近的餐馆。甚至是图书馆里的书籍也可以被贴上带有传感器的标签。这样就不用担心被放错书架的书本难以找回了。
人们推测,传统的 DBMS 将不再适用于这种新型的监控应用。 事实也是如此。根据线性道路基准来测试,传统解决方案比专用流处理引擎慢了近一个数量级【9】。传统的 DBMS 技术不适用于流应用。当前流数据应用领域的研究也论证了这个观点。接下来我们将分享一个我们在处理金融信息流(financial feed)的应用上的经验。
#3.2 一个现有的应用:金融信息流处理
大多数大型金融机构会订阅提供市场活动实时数据的信息流,特别是新闻、已完成的交易、出价和要价等。路透社、彭博社和 Infodyne 就是提供这类信息的供应商。金融机构有各种各样的应用用于处理这些信息流,包括实时业务分析系统、电子交易系统、用于确保所有交易符合各种公司和 SEC 规则的系统,以及用于计算实时风险和外汇汇率波动的市场披露的系统。用于实现这类应用的技术总是需要“自力更生”,因为并没有现成的系统软件产品供应用专家使用。
为了更深入地探索信息流的处理问题,接下来我们会详细地描述一个特定的示例应用。这个应用是由一家大型共同基金公司指定的。该公司订阅了几份商业信息流,并且有一个生产应用用于监控所有信息流中是否存在滞后数据(late data)。从而当发现某一信息流存在滞后现象时,该公司能够提醒交易者,该信息流所提供的数据不可信。但是,该公司当前使用的解决方案在性能和灵活性无法满足需求,于是该公司要求试点使用流处理引擎。
该公司的工程师指定了一个当前应用的简化版本,用于比较他们当前使用的系统和流处理引擎之间存在的性能差异。根据他们的具体要求,他们希望能在单台 PC 上为其应用的子集获得最大的消息处理吞吐量,由两条信息流组成且两条信息流中的数据来源于两个交易所,共涉及4500只证券,其中500只交易非常频繁。针对一只交易频繁的证券而言,如果从某个交易所收到的两个连续 tick(交易快照)之间间隔超过了5秒,那么后收到的那一条 tick 则被视为滞后 tick。对于另外4000只证券而言,这个时间间隔为60秒。
由于使用了两家信息流供应商,该公司希望一旦有来自任一供应商的一条滞后 tick,都能收到一条告警信息。该公司还希望为每个提供商分别维护一个计数器。当某一供应商有超过100条滞后 tick 时,该公司希望能够收到一条特殊预警信息,提醒该供应商的 tick 信息可信度极低,从而该公司能够控制后续来自该供应商的 tick 报告。
该公司的最后一条要求是,希望从两个交易所(比如纽约证券交易所和全美证券交易商协会)中累计滞后 tick 数,而不用管具体滞后 tick 源于哪个信息流供应商。从而,总滞后 tick 数达到100和某一信息流供应商的滞后 tick 数达到100所产生的提醒消息是不同的。所以,该公司一共需要四个计数器。一旦任一计数器计数到100,便会触发对应的预警消息。这个任务的查询图的抽象表示如图3所示。
虽然这个示例应用只是实际生产系统中使用的应用逻辑的一个子集,但它代表了一个易于指定的任务,其性能很容易被测量,因此极具代表性。现在我们来对比下这个示例应用在流处理引擎上和关系型 DBMS 上的速度。
上一章中讨论的示例应用是在 StreamBase 流处理引擎【5】中实现的,它基本上可以看作是Aurora 【8,13】的商业、工业级版本。我们使用的2.8 Ghz 奔腾处理器规格为512 MB 内存,拥有一个 SCSI 磁盘。在 CPU 饱和之前,图3中的工作流可以以每秒160,000条消息的速度执行。相比之下,StreamBase 的工程师使用主流的商业关系型 DBMS 实现同一个应用时,每秒只能处理900条消息。
在本章中,我们将讨论造成这种性能差异的主要原因。正如我们下面即将讨论的,差异的产生与入站处理模型、正确的流处理原语以及 DBMS 处理与应用处理的无缝集成有关。另外,我们还考虑了另一主要因素:事务性操作。
#4.1 “入站”与“出站”处理
我们称之为“出站”处理的流程是 DBMS 模型的基本组成流程,如图4所示。具体可分为三步:
1. 将数据插入(updates)数据库。
2. 为数据创建索引并提交事务(pull processing),从而后续步骤可以使用该数据。
3. 将处理结果(results)呈现给用户。
这种“存储后处理(process-after-store) ”的模式是所有传统 DBMS 的核心,毕竟 DBMS 的主要功能是接收数据并保证数据不会丢失。
当然,也许有人会问“DBMS 能支持入站处理吗? ”DBMS 最初被设计为出站处理引擎,但是在多年以后才想到将触发器移植到它们的引擎上进行补救。使用触发器存在许多限制,比如每个表允许使用的数量。另外,使用触发器的安全性没法保证,我们无法确保触发器不会进入无限循环(infinite loop)中。总体看来,关于触发器的编程支持少之又少,甚至于没有。例如,用户无法查看到应用中存在哪些触发器,也无法通过图形用户界面将触发器添加到表中。此外,DBMS 为常规表提供了虚拟视图和物化视图,但却不为触发器提供。最后,触发器在现有引擎中经常会出现性能问题。当 StreamBase 的工程师尝试在信息流告警应用(feed alarm application)中使用触发器时,仍然无法实现高于每秒900条消息的性能。总之,触发器作为现有设计中的“外来者”,在当前的系统中处于二等公民的地位。
因此,关系型 DBMS 其实质是出站引擎,仅有限地提供一些入站处理能力。相比之下,Aurora 和 StreamBase 之类的流处理引擎基本上是入站处理引擎。入站引擎与出站引擎是完全不同的。例如,出站引擎使用“拉取(pull)”处理模型,即:当一个查询被提交之后,由引擎来实现从存储中高效拉取满足查询的记录。相反,入站引擎使用“推送(push)”处理模型,即当一个查询被提交之后,由引擎来按照应用中指定的处理步骤来高效地推送传入消息(incoming message)。
出站引擎和入站引擎还有一个很大的区别。出站引擎先存储数据然后根据存储的数据执行查询。而入站引擎恰好相反,入站引擎先存储查询,然后通过查询传递传入消息。
很明显,构建一个同时拥有出站和入站引擎能力的引擎是拥有可行性的也是值得研究的。同时,DBMS 针对出站处理进行了优化,而流处理引擎则针对入站处理进行了优化。在信息流告警应用中,这种原理上的差异是造成我们观察到的性能差异的主要原因。
#4.2 正确的原语
SQL系统包含一个复杂的聚合系统,用户可以通过该系统对数据库表中的记录分组进行统计计算。下述代码提供了一个标准的示例:
Select avg (salary)
From employee
Group by department
当执行引擎处理表中的最后一条记录时,它可以针对每组记录分别进行聚合计算。然而,这种结构在流应用中并没有什么优势。在流应用中,流永远不会结束,没有“表结束(end of table)”的概念。
因此,流处理引擎用时间窗口(window)的概念对 SQL(或其他一些聚合语言)进行了扩展。StreamBase 支持根据时钟时间、消息数量或其他属性中的断点来定义窗口。在信息流告警应用中,每个流中最左边的框就是这样一个聚合框。该聚合按符号对股票进行分组,然后为每一只股票定义窗口为 tick 1和2、tick 2和3、tick 3和4等。通常情况下,这种滑动的窗口(sliding windows)在实时应用中非常有用。
此外,StreamBase 将聚合构建成能够智能地处理滞后、失序或丢失的消息的操作。在信息流告警应用中,客户最感兴趣的是查找最新的数据。StreamBase 允许窗口聚合可以携带两个附加参数:timeout 和 slack。timeout 参数指示 StreamBase 执行引擎关闭一个窗口并发出一个值,即使窗口并不满足关闭条件。该参数能够有效地应对元组滞后和缺失问题。slack 参数指示执行引擎保持一个窗口的打开状态即使该窗口满足关闭条件。此参数解决了元组的无序接收问题。这两个参数允许用户指定如何处理流异常并且可以有效提高系统弹性。
在信息流告警应用中,每个窗口为两个 tick,但 tick 有超时设置,超时时间为5秒或60秒。如果两个连续 tick 之间的间隔时间超过用户定义的超时阈值,对应窗口将会被关闭。超时设置能够有效地发现滞后数据,这算是高度调整的集合逻辑所带来的一个意外之喜。在示例应用中,每个聚合后的框在丢弃掉有效数据之后,只保留超时消息。应用的其他部分对这些超时执行必要的分类记录(bookkeeping)。
系统底层使用正确的原语可以实现非常高的性能。相比之下,关系型引擎不包含这样的内置结构。用传统的 SQL 模拟这种内置结构的效果不仅繁冗复杂,并且导致了第二个显著的性能差异。
SQL 支持添加时间窗口,但是这样做对存储的数据没有任何意义。因此,窗口构造必须集成到某种入站处理模型中。
#4.3 DBMS 处理和应用逻辑的无缝集成
关系型 DBMS 都被设计成了 C/S (client-server)结构。这个模型中存在许多客户端应用。应用的作者可能是任何人,因此通常是不可信的。出于安全性和可靠性考虑,这些客户端应用与 DBMS 运行在不同的地址空间中。但是这么做会导致频繁的进程切换。
而信息流应用是一个嵌入式系统,由一个受信任(trusted)的人或团体编写。整个应用包括:
1. DBMS 处理,例如聚合和过滤框(filter box)
2. 控制逻辑,用于将消息导向正确的下一个处理步骤
3. 应用逻辑
在 StreamBase 中,这三种功能可以自由分布。应用逻辑通过自定义框实现。在我们使用的例子金融信息流处理应用自定义了框Count100。实际代码如图 6 所示,由四行 C++ 代码组成。计数到100,并设置了一个标志以确保发出消息的正确性。应用的支持逻辑通过允许在一个过滤框中使用多个谓词来实现,从而可以有多个退出弧(exit arcs)。因此,除了过滤流之外,过滤框还负责执行“if-then-else”逻辑。
实际上,信息流告警应用混合了 DBMS 风格的处理、条件表达式和传统编程语言中的自定义函数。 StreamBase 在单个地址空间内执行这种组合,无需进行任何进程切换。Rigel【23】和 Pascal-R【25】在多年前就提出了这种将 DBMS 逻辑与传统编程工具无缝集成的设想,但从未在商业化的关系型系统中实现。
取而代之的是,主流供应商实现了存储过程,这是一个非常有限的编程系统。最近,市场上出现了使用对象-关系(object-relational)引擎的刀片服务器和扩展程序,它们比存储过程更强大,但仍然不支持灵活的控制逻辑。
嵌入式系统不需要 C/S 架构的 DBMS 进行保护,这种两层架构只会造成更大的开销。这是造成我们观察到的性能差异的第三个原因。
另一个集成问题是流式应用中状态信息的存储。信息流告警应用不涉及该问题。大多数流处理应用都需要保存一些状态信息,小到几 MB,大到几千 GB。 这些状态信息可能包括:
1. 参考数据(即,市场对哪些股票感兴趣);
2. 转换表(以防不同信息流来源使用不同的符号代表同一股票)
3. 历史数据(例如,“在去年中,每天观察到多少个滞后 tick?”)。因此,大多数流处理应用都要求对数据进行表格式存储。
StreamBase 在 BerkeleyDB 中内嵌了状态存储功能【4】。然而,在 StreamBase 地址空间中调用 BerkeleyDB 和在不同的地址空间中以 C/S 模式调用 BerkeleyDB 之间存在大约一个数量级的性能差异。这也是为何要在一个地址空间中混合 DBMS 和应用处理来避免进程切换的另一个原因。虽然有人可能会建议通过增强 DBMS 的编程模型来解决这个性能问题,但是 DBMS 采用 C/S 模型有其必要性,因为大多数业务数据处理应用需要这种模型来提供保护。存储过程和对象-关系刀片都是一种将部分客户端逻辑移至服务端以提高性能的尝试。为了更进一步,DBMS 必须实现一个嵌入式和一个非嵌入式模型,并且这两个模型使用不同的运行时系统(runtime system)。这种做法等同于放弃了“One Size Fits All”策略。
相反,信息流处理系统都是嵌入式应用。应用和 DBMS 的作者是相同的,并由外部数据源驱动,而非由人工输入的事务驱动。因此,没有理由针对应用给 DBMS 提供保护,且二者在同一地址空间运行是完全可以接受的。在嵌入式处理模型中,对应用逻辑、控制逻辑和 DBMS 逻辑进行自由混合是合理的,而 StreamBase 也确实完成了该实现。
#4.4 高可用
许多基于流的应用都要求高可用性和24/7不间断运行。标准 DBMS 日志记录和崩溃恢复机制(例如【22】)不适合流数据市场,主要原因如下:
首先,基于日志的恢复可能需要几秒到几分钟。在此期间,应用将被“关闭”。这种行为在许多实时流领域(例如,金融服务)中显然是不能接受的。第二,如果系统崩溃,必须采取措施来缓冲传入数据流,否则这些数据将在恢复过程中永久丢失。第三,DBMS 恢复将只处理表格状态,忽略了操作员(operator)状态。例如,在信息流告警应用中,计数器并未存储在表格中。如果系统崩溃,计数器的状态信息将会丢失。一个简单的解决方法是将所有操作符状态强制转换到表中,使其适用于 DBMS 风格的恢复;但是,这种解决方案会大大降低应用的速度。
一个常见的高可用替代方案是使用依赖于串联式进程对(Tandem-style process pair)【11】的技术手段。该替代方案的基本思路是,在系统崩溃的情况下,应用通过故障恢复,恢复到备机(backup machine)上运行。备机通常以“热备(hot standby)”方式部署,并和主机(active machine)之间保持极小的时延。这种方法免除了日志记录的开销。再以 StreamBase 为例,StreamBase 关闭了 BerkeleyDB 中的日志记录。
与需要精确恢复以确保正确性的传统数据处理应用不同,许多流处理应用可以容忍并能受益于没那么严格的恢复概念。换句话说,故障恢复并不总是需要“完美”的。监控应用操作的数据流的值会被定期刷新。如果由于系统崩溃造成的业务终端时间很短,这种程度的元组丢失对于监控应用而言通常是可以容忍的。即,如果在故障恢复期间丢失了信息流告警应用中的几个 tick,正确性可能仍然可以保障。相比之下,一些用于在某些事件组合发生时触发告警的应用则要求没有元组丢失,但可以容忍短暂的重复。例如,患者监控应用可能能够容忍元组重复(“心率是79”),但不能容忍元组丢失(“心率已经变为零”)。当然,总有一类应用需要强大、精确的恢复保证,例如基于单个股票交易执行投资组合管理的金融类应用。
因此,当应用对准确性要求不那么严格的时候,可以考虑采用低开销的简化版的故障恢复方案。近期出现了很多关于如何在流领域实现高可用的详细研究【17】。
# 4.5 同步
许多基于流的应用依赖于共享数据和计算。共享数据通常包含在一个表中。有的用户会查询表中的更新,而有的仅读取数据。例如,线性道路应用要求使用车辆位置数据来更新高速公路使用情况的统计数据,然后读取这些数据来确定高速公路上每个路段的过路费。因此,消息隔离是一个基本需求。
传统的 DBMS 使用满足 ACID 属性的事务来实现多个用户之间提交的并发事务的隔离。在非多用户的流系统中,这种隔离可以通过简单的临界区(critical section)有效地实现,临界区可以通过轻量级信号量(semaphore)来实现。由于完全符合 ACID 属性的事务不再是必需,使用重量级基于锁的机制也并非必要。
总之,大多数流处理应用不需要 ACID 属性,因此可以使用更简单、更专业的性能结构。
上一章已经指出了一系列造成专用流处理引擎和传统 DBMS 之间的性能差异的架构因素。这些设计上的不同选择同时还导致了两种引擎之间巨大的内部差异。事实上,StreamBase 使用了完全不同于传统的 DBMS 的运行时代码。这样做的结果是大大提高了一类实时应用的性能。这些考量将导致针对流处理使用单独的代码,当然产生这种情况的前提是市场足够大。
在本章中,我们概述了其他几个市场。这些市场有着一个共同点:有专用数据库引擎的潜在需求。
# 5.1 数据仓库
在第2章中我们讨论到的 OLTP 和仓库数据库系统之间的架构差异只是冰山一角,随着时间的推移,二者之间会产生更大的差异。接下来我们讨论下我们可能关注到的最大的架构差异:列式存储和行式存储的选择。
所有主流DBMS 供应商都实现了面向记录的存储系统,其中记录的属性被连续地放置在存储中。使用这种“行式存储”架构会将单个记录的所有属性推送到磁盘,产生一次磁盘写操作。因此,这样的系统是“写优化的”,这种架构能够保障极高的记录写入性能。不难看出,写优化的系统非常适合 OLTP 风格的应用,这也是大多数商业 DBMS 采用这种架构的主要原因。
相比之下,仓库系统则需要“读优化”,因为其大多数工作负载由涉及针对大量历史数据的即席查询组成。在这样的系统中,一个能够把一个属性对应的所有行中的值进行连续存储的“列式存储”模型更为高效(已被 Sybase IQ【6】,Addamark 【1】和 KDB【2】所证明)。
使用列式存储架构的 DBMS 只需要读取和处理查询中指定的属性,可以避免将其他不相关的属性带入内存。 鉴于具有数百个属性(包含许多空值)的记录越来越常见,这种架构对于仓库工作负载来说能够提供相当大的性能优势,因为仓库场景下的典型查询包含对大型数据集上的少量属性进行聚合计算。本文的第一作者正在研究探索列式存储系统的性能优势。
#5.2 传感器网络(Sensor networks)
传感器网络中的处理节点用于管理网络的传感器。 而在这些处理节点上运行传统 DBMS 是一个不切实际的想法。这些新兴的设备网络平台的应用正在被探索中,包括、环境和医疗监控、工业自动化、自主机器人团队和智能家居【16、19、26、28、29】。
为了能在通信和能源领域开发出这些系统的全部潜力,组件被设计成无线的。在这种环境下,带宽和功率成为需要省着用的关键资源。此外,与处理或存储访问相反,通信是能源的主要消耗者。因此,标准的 DBMS 优化策略并不适用,需要另寻突破口。事务处理能力也并未纳入仓库系统的考虑范围。
一般来说,仓库系统需要设计灵活、轻量级的数据库抽象(如 TinyDB【18】),针对数据移动进行优化,而不是数据存储。
#5.3 文本搜索(Text search)
目前的文本搜索引擎都没有使用 DBMS 技术进行存储,即使它们处理的是海量的、不断增长的数据集。 例如,Google 构建了自己的存储系统(称为 Google File System,GFS【15】),它的性能优于传统的 DBMS 技术以及文件系统技术。产生这种性能优势的原因我们已经在第4章中讨论过了。
一个典型的搜索引擎工作负载【12,15】由需要清洗并合并到现有搜索索引中的入站流数据(来自网络爬虫)和现有索引上的即席查找(ad hoc look-up)操作组成。尤其是,大多数写操作只涉及数据追加(append),而大多数读操作都是顺序读(sequential read)。对同一文件的并发写(即追加写)是获得良好性能的必要条件。最后,由商品零件组成的大量存储机器确保了故障是常态而非例外。因此,高可用是设计考虑的一个关键因素,并且只能通过快速恢复和复制来实现。
可以很明显的看出,这些应用的特征与传统的业务处理应用有很大的不同。这样就导致即使一些 DBMS 具有内置的文本搜索能力,但却过于笨重、不灵活,依然无法满足这个领域对于性能和可用性的要求。
#5.4 科学的数据库(Scientific databases )
各种类型的传感器不断地收集大量数据,这些传感器连接着卫星和显微镜等设备,或者由高分辨率科学和工程人工模拟生成。
关于这类数据集的分析能够帮助我们更好地了解物理现象,并且在许多科学研究领域变得越来越普遍。对这些庞大数据库的有效分析和查询需要高效的多维索引结构和特定应用的聚集技术。 此外,对高效的数据存档、分级、沿袭和错误传播技术的需求可能会在这个重要领域中产生对另一个专用引擎的需求。
#5.5 XML 数据库(XML databases)
半结构化数据无处不在。但是这类数据不能立即适应关系模型。关于怎么存储和操作 XML 数据才是最好的方式存在着巨大的争议。尽管有些人认为关系型 DBMS(带有适当的扩展)是可行的,但其他人会认为需要一个专门的引擎来存储和处理这种数据格式。
大多数流应用需要以下三项基本服务:
- 消息传输: 许多流应用要求数据能在多个分布式机器间高效、可靠地进行传输。原因有三:第一、数据源(source)和传输的目的地(destination)通常在地理上是分散的。第二、高性能和高可用要求使用多台服务器。第三、几乎所有大型企业系统都是一个嵌入了 SPE (单对以太网,single-pair Ethernet)的复杂业务应用网络,而这些应用分散运行在大量的机器上。因此,来自外部应用的输入和输出消息在经过 SPE 时需要正确地路由到恰当的其他外部应用中
- 状态存储:如4.3节所述,除了最简单的应用之外,所有应用都需要存储状态。状态通常存储在只读引用表、只读历史表以及读写变换(read-write translation, 如哈希)表中。
- 应用逻辑的执行:许多流应用要求特定域的消息处理中穿插查询。一般来说,仅使用内置查询原语来表示这种应用逻辑既是不可能的也是不现实的,比如通过解读遗留代码。
流处理应用的传统设计将整个应用逻辑分布在三个不同的系统中:
1. 消息传递系统(例如 MQSeries、WebMethods 或 Tibco):将各组成系统可靠地联系起来,通常使用发布/订阅范例;
2. DBMS(如 DB2 或 Oracle):保证状态信息的持久性;
3. 应用服务器(如 WebSphere 或 WebLogic),为一组定制编码的程序提供应用服务。图7展示了该种三层配置的架构。
这种将所需功能分散在三个重量型系统软件上的设计在性能上却不尽如人意。例如,每个需要查找状态和应用服务的消息都需要在这些不同的服务之间进行多次切换。
为了说明每条消息的开销,我们来看看消息处理时涉及的步骤。在步骤1中,传入的消息首先经过总线,被总线转发给自定义应用代码,后者清理并处理该消息。如果消息需要与历史数据相关联或者需要访问持久数据,那么则会进行步骤2和3,向 DB 服务器发送一个请,DB 服务器会访问 DBMS。步骤4和5描绘了请求响应路径,方向和应用代码路径刚好相反。在最后一步(步骤6) 时,消息处理后的结果被转发到客户端的任务 GUI。总的来说,处理一条消息涉及6次“跨界(boundary crossing)”。 除了引发明显的上下文切换之外,每次从消息总线中提取消息并传递到消息总线时,还需要使用恰当的适配器将消息即时地转换为系统的本机格式或从系统的本机格式转换为目的端格式。这就导致了极低的有效工作:开销比(useful work:overhead)。即使对消息进行一定程度的批处理,开销也依然很大并且限制了系统的性能实现。
为了避免这种性能损失,流处理引擎必须在一个系统软件中提供所有三种服务,该软件在其运行的每台机器上作为一个多线程进程执行。因此,SPE 必须具有 DBMS、应用服务器和消息传递系统三要素。实际上,一个 SPE 应该“在一个屋檐下”提供所有三种软件的功能。
这一观察提出了这样一个问题,即当前将系统软件分解成组件(例如,应用服务器、DBMS、ETL 系统、消息总线、文件系统、web 服务器等)的做法是否是最优解。毕竟,这种特殊的分解方案一部分是历史产物,而另一部分则是偶然的营销事件。这样看起来系统服务的其他分解方案似乎也是合理的。这也意味着组件定义和分解在未来将迎来巨大的发展。
总之,未来可能会出现大量应用于特定领域的数据库引擎。 这些引擎将提供不同功能。这使我们想起了“愿你生活在有趣的时代”这句祝福。我们认为 DBMS 市场正变得愈发有趣起来。许多已有的和新出现的应用都能从数据管理、处理原则和处理技术手段中受益。同时,这些应用与业务数据处理有很大的不同,而且应用之间也存在着巨大的差异。这就导致使用一行代码来支持所有应用成为了一个不切实际的想法。在这种情况下,“One Size Fits All”策略的结局只能是逐渐被市场淘汰。