在过去几年间,已经有多个项目使用了Scala进行大规模数据储存及分析平台的设计。
BBC在设计公司内部的RDF数据储存系统时就使用了Scala以及Scalatra HTTP框架,这套内部系统可用于查询BBC新闻与体育所有文章中的链接数据。
在整个2014年间,业界对于Apache Spark的兴趣达到了一个高峰,这是一套使用Scala编写的数据分析工具。Spark中包含了一套由Spray编写的HTTP接口,它的后端使用了Akka用于并行处理。
迈凯伦应用技术(MAT)在他们的数据分析平台中也使用了Scala、Scalatra和Akka,作为整个平台的构建基础。
为什么这些项目会选择Scala呢?
为了找到这一问题背后的原因,我们与来自MAT的高级软件工程师Andrew Jayne进行了一次对话,了解了后者使用Scala创建一个自定义的高性能数据储存系统的经验。
迈凯伦应用技术位于伦敦附近,它源自于母公司迈凯伦几十年以来在F1赛车方面的经验。从母公司分离之后,MAT也将F1的相关经验应用在其它产业上。
由于MAT深厚的F1背景,因此它们在根据数据进行分析以及进行快速决策方面具备很强的技术实力,例如它们的时间序列数据存储系统能够分析一场F1赛事中车辆的数据流。不过,他们也发现这套序列分析技术在与传感器设备相结合的情况下,往往能够应用在赛车之外的其它许多场景中:他们已经对制造业、传输业、能源业以及医疗保健行业的部门提出了这项建议。
为了支持MAT的这项咨询工作,他们的工程师创建了一个序列数据平台,运行于AWS之上。
InfoQ:你是否能为我们概述一下MAT序列数据储存系统的作用是什么,以及如何使用它?
Jayne:这是一个大容量、高性能的排序式键-值存储系统,它能够保存读-写的一致性,并且具有一个专门为实时及历史序列数据(根据时间、距离、深度等进行索引)设计的特性集。它允许我们以一种版本控制的方式进行微批量及批量处理,并且可以通过某个REST API对我们的数据分析平台的输入与输出的所有序列进行持久化。这方面有一个例子,我们正在为葛兰素史克(GSK)设计对渐冻症(ALS)患者进行监控与分析的系统,这是基于传感器设备的,通过临床实验,让我们能够得到在实验中患者的病症得到改善,或产生恶化的反馈信息。
InfoQ: MAT序列数据存储系统是否是用于分析F1相关的时间序列数据的目的,还是更为一般性的目的而存在的?
Jayne:设计这套系统的目的是作为一种一般性的解决方案,对序列数据进行保存与查询,它基于我们在赛车运动、竞技体育、医疗以及能源行业的经验。我们在这些项目中都面临着类似的问题,因此急需一种存储方案,它能够保持数据完整性,同时又能帮助我们解决这些问题。比方说,如果你的数据来自于某个产生了时钟漂移的设备,你该怎么做?如果出于带宽的限制,使得上传的数据只具有较低的分辨率,那么我们又该如何确保数据的高精度?如果在一段长时间的网络断线之后,某个数据源又开始发送数据,那么又会发生什么情况?
InfoQ:这套系统主要针对怎样的数据集,目标的数据量是什么?
Jayne:它可以用于n维的数值数据,其中包含一个数值型的、严格增长的键。目前我们的用例除了我们的数据分析结果之外,还包含病患监控设备的传感器数据、钻井作业以及数据中心。
InfoQ:为什么选择Scala?你的技术背景是从Java转移而来的吗?这门语言有什么地方吸引了你?
Jayne:整个团队具有多种语言的背景。在2012之前,我们使用了Java、C#和MATLAB。随着Typesafe Stack 2.0发布之后,我们就开始采用Scala作为我们的服务以及web应用程序的后端语言。
当时Scala最吸引我们的地方在于它的可伸缩性,例如可以通过使用Actor、不可变对象以及函数式编程简化并发处理。此外还有一些原因,比如能够以更加算术式的语法表达程序、模式匹配,并且能够减少样板代码。不过,如果要牺牲Java的互操作性,那我们也不可能会选择Scala。函数式编程的方式非常新颖、痛快,也为我们展现了更多创新的机会。这个选项很符合团队的文化与心态。
InfoQ:为什么这套系统没有选择Java?
Jayne:我们已经有了一个用Java编写并基于Hadoop的组件。虽然我们也可以在这个组件的基础上打造新系统,但其它所有后台组件都是用Scala编写的。我们将此看做一次开展研究工作的好机会,很快我们就明白,要支持我们所设计的架构,可以选择的类库与技术有哪些了。基于这些研究的经验,我们意识到可以用函数式编程简化我们的现有功能。虽然Java很易于使用,但它的语法过于啰嗦,会妨碍我们保持代码清晰度的意图。我们已经在商业项目中使用Scala有很多年了,我们发现能够与创建这门语言以及核心类库的人们进行交流是很有价值的。
InfoQ:其它的一些时间序列数据库(TSDB)缺少了什么功能?为什么没有选择KairosDb、Druid、InfluxDb、OpenTSDB,或其它任何一种现有的TSDB?
Jayne:对于我们的业务来说,数据完整性与数据管理是至关重要的,并且我们需要对这些数据进行某些程度的保证,以符合代码端的期望。仅仅实现备份与记录日志是不够的。受到了Git的影响,我们最终设计了一套数据储存系统,它具有基于提交的版本控制能力,以及基于树状的组织结构
我们的验收场景也提出了额外的挑战,例如:
原子性:不允许在查询系统时返回不一致的状态。
一致性与正确性:查询应当是可重复的,并且始终返回一致的、正确的数据。
接近实时的写操作:支持每秒几百万次写入操作(数据来自于高频率及可变频率的传感器),并且在数据写入后的几秒钟之内就可用于查询。
查询性能:通过在整个频道中提供可预测且快速的查询,支持能够感知延迟的数据分析能力,包括:数据准备(即插值);子集(即窗口、范围、偏移、限制);聚合(即平均值、最小值、最大值等等);预测(即推断、回归、关联)。
语言无关性:使用REST API与ETag缓存。
通用的数据格式:数据应当与现有的工具兼容,例如CSV、JSON等等。
部署复杂性:系统应当使用现有的云端基础设施(AWS),并且能够以最小的代价创建新的数据存储。
这些需求中有很大一部分已经在现有的TSDB中已经得到解决,其中某些TSDB对这些问题的解决方案更为出色。但是,我们所需的某些查询是其它数据库所不支持的,也很难对其进行改进。主流的TSDB都是在其它数据库系统的基础上创建的(例如HBase),它们的复杂性已经超出了我们的用例的需求。通过使用一种更简化的、量身订做的存储机制,并利用分布式特性,我们就能够对数据的组织进行优化,更适用于我们所执行的查询。这种方式也让系统管理员的工作量以及运行时的成本减至最小。最重要的是,其它数据库都无法提供这种对数据的版本控制机制,而我们在开发与生产环境中都需要这种机制。
InfoQ:说到REST API,你在项目中使用了Scalatra作为REST HTTP接口,原因是什么?
Jayne:Scalatra的维护工作做得很好,它的发布策略也很明智,我们之前在使用其它框架时曾在这一点上吃过亏。这个框架有一套十分简单的领域特定语言(DSL),我们只需要进行很少的工作,即请求处理与测试。并且它还能够使用我们选择的其它类库,进行诸如序列化等工作。Scalatra能够简单地部署到某个servlet容器中是它的一大优点,并且它的性能也十分出色。
InfoQ:你提到了Actor。那么Akka这个选择在实际运行中效果如何?我听说某些人对它爱不释手,而另一些人则表示自从他们远离了Akka之后,代码就变得简单了许多倍。那么使用它的挑战又来自于哪里呢?
Jayne:我们在数据储存系统中使用Akka管理异步的请求,因为一个请求的生命周期中的大部分都是进行I/O操作。它目前还不是一种基于事件的I/O技术,但我们认为Akka的优点在于它的调度器以及对栈溢出的处理。
我们使用了actor模型将某一问题分解为多个子系统集,每个子系统维护自己的状态,负责自己的可伸缩性。由于在设计时就考虑到了默认的分布式架构,因此你可以利用它的位置透明性特征实现纵向与横向的扩展。不过,由于失去了正式的接口,这一点也让我们产生了动摇,因为这意味着我们将失去编译器检查的优势。一种妥协方案是使用正规的协议,让actor保持简洁,并让这些协议在我们的设计与测试中明确地表现出来。编写清晰的、简明的测试不是一件简单的事,而在任何并发系统中进行调试都是一个很大的挑战。
InfoQ:这套数据库能够在多个节点间进行集群化吗?它的横向扩展能力如何?这一点与你们选择了Scala的决定是否有关,还是说无论使用哪一种语言结果都一样呢?
Jayne:目前来说,它仅在数据处理量以及故障转移方面做到了可伸缩性,因为API终结点与数据的位置是在一起的。接下来在改善可伸缩性方面的工作是移除这一限制,这样我们就可以在REST API端使用一种灵活的负载均衡器,并将其从数据储存层中分解出去。
使用Scala进行开发的速度是非常之快的,并且使用函数式编程能够让许多最复杂的处理过程也变得十分清晰。默认的不可变性也鼓励更清晰的编程模式,并且更容易实现对系统的原子性与一致性的强制实施。不过,在Java中也没有什么不能做到的,只是我们觉得Scala是最适合我们团队的工具。
关于作者
Dave Hrycyszyn
Head London的技术总监,这是一家位于英国的数字产品与服务代理商。Dave是 《Scalatra in Action》一书的作者之一,也是创建Scalatra这个微框架的团队的成员之一。他在自己的博客constructiveproof.com上撰写了大量有关于Scala、API和与数据相关的主题文章。