Elasticsearch是一款非常流行的日志检索和分析工具,尤其在实时性、扩展性、易用性和全文检索方面有着非常优异的综合表现。知乎上有一篇文章,Golion:降维打击!使用ElasticSearch作为时序数据库,并且取得了非常不错的效果。很多知乎用户不禁询问,Elasticsearch是否可以用于海量金融数据的存储和分析?

为此我们对DolphinDB和Elasticsearch在不同规模的金融数据集做了综合的对比测试。测试的内容包括I/O,磁盘空间占用,内存消耗,数据库查询(过滤查询和分组统计)4大项。测试结果没有意外,在金融数据处理领域表现十分抢眼的时序数据库DolphinDB完胜Elasticsearch。

  • 分组统计(聚合计算),DolphinDB的表现优于Elasticsearch 10倍左右,而且随着数据集的增大优势更明显。特别的,当测试用例涉及时间类型字段时,DolphinDB的表现尤为突出。
  • 简单的过滤查询,DolphinDB的性能是Elasticsearch的100倍。
  • 在数据导入方面,Elasticsearch的耗时是DolphinDB的25~75倍,并且随着数据集增大,有变大的趋势。
  • 在磁盘空间占用上,DolphinDB做到了对原始数据的压缩,而Elasticsearch为了维护文档的索引等信息(不包括临时数据),在磁盘上占用了比原始数据更大的空间。总体差距在10倍左右。

1. 系统介绍

1.1 DolphinDB简介

DolphinDB是一款分析型的分布式时序数据库,采用列式存储,内置流数据处理引擎,并行计算和分布式计算引擎,并提供分布式文件系统,支持集群扩展。DolphinDB以C++编写,响应速度极快。提供类SQL和Python的脚本语言对数据进行操作。提供其它常用编程语言的API,方便与已有应用程序集成。在金融领域中的历史数据分析建模与实时流数据处理,以及物联网领域中的海量传感器数据处理与实时分析等场景中表现出色。

1.2 Elasticsearch简介

Elasticsearch是一个基于Lucene的搜索服务器,它是基于本地磁盘存储数据的分布式系统,并面向文档进行存储。它和传统的数据库有以下类似的对比关系:

Relational DB =>Databases =>Tables => Rows => Columns

Elasticsearch =>Indices=>Types=>Documents => Fields

Elasticsearch集群可以包含多个索引(Indices),对应DolphinDB的数据库;每一个索引可以包含多个类型(Types),对应DolphinDB中的表;每一个类型包含多个文档(Documents),对应DolphinDB数据中的行;然后每个文档包含多个字段(Fields),对应DolphinDB中的列的概念。

2. 系统配置

2.1 硬件配置

本次测试的硬件配置如下:

设备:DELL OptiPlex 7060

CPU:Inter(R) Core™ i7-8700 CPU @ 3.20GHz,6核心12线程

内存:32GB

硬盘:2TB机械硬盘

操作系统:Ubuntu 16.04 x64

2.2 环境配置

本次的测试环境为单服务器下的多节点集群。为了在单机环境下最大限度地发挥出两者的性能,需要对DolphinDB以及Elasticsearch进行节点参数的设置。设置DolphinDB的数据节点的个数为4个,单个数据节点最大可用内存设置为7 GB。设置Elasticsearch的节点个数为4个,由于Elasticsearch基于Lucene,故需要分配一定内存用于Lucene中的段被加载入内存中,这对Elasticsearch的性能也会造成很大的影响,本次测试分配8 GB内存给Lucene,并设置Elasticsearch中的单个节点的最大可用内存为6 GB,且禁止swapping。

3. 测试数据集

为了更加全面地测试DolphinDB和Elasticsearch的性能,我们使用了三个不同规模的股票数据集。数据表CN_Stock中包含了从2008.01.01到2017.12.31中国沪深股票的每日报价数据。数据表US_Prices中包含了从1990.01.02到2016.12.30美国股票市场的每日报价数据。数据表TAQ中包含了2007年8月份的4天美国股票市场level1的高频数据,共60.6 GB。测试数据集的概况如下表所示:

d0f5b2ac7fb475b20c61de31564ee331.png

测试数据集在DolphinDB和Elasticsearch中各个字段的数据类型如下所示:

(1)CN_Stock表数据类型映射

edc651a7c632e97a2a3961fcd86c4795.png

(2)US_Prices表数据类型映射

17e4aff6255d2bc11c4973b66b0d3405.png

(3)TAQ表数据类型映射

5263427d6736b3ab2ea8ca44d9ea5b9b.png

4. 分区/分片方案

DolphinDB database 提供了灵活的分区机制,包括值分区,范围分区,列表分区,哈希分区和组合分区,而Elasticsearch仅支持基于哈希的分片机制。

在DolphinDB中,对于表CN_Stock,按时间每半年作为一个分区,共分成了20个分区;对于表US_Prices,按时间每年作为一个分区,共分成27个分区;对于表TAQ,采用日期、股票代码组合分区方式,共100个分区。副本个数设置为1。

Elasticsearch仅允许定义分片的个数。对于表CN_Stock和表US_Prices,定义分片个数为4;对于表TAQ,定义分片个数为100。副本个数设置为1。

5. 对比测试

我们从数据库查询性能、I/O性能、磁盘占用空间、以及内存消耗等方面对DolphinDB和Elasticsearch进行了对比测试。

5.1 数据库查询性能测试

DolphinDB脚本语言支持SQL语法,同时还在其基础上进行了一定程度的扩展,功能更加强大。而在Elasticsearch中,需要安装插件来进行SQL语句查询,同时也提供了基于JSON数据格式的DSL(Domain Specific Language特定领域语言)语言来进行查询,本次测试采用DSL语言。

Elasticsearch的主要应用场景为搜索引擎,它支持模糊查询,对于普通的query,Elasticsearch返回的查询hits默认仅为10条;对于聚合查询,其返回的buckets的默认大小也为10。而DolphinDB中每次查询返回的结果都是全部的结果,不存在模糊查询的情况。

在Elasticsearch的聚合查询中,返回结果中存在字段doc_count_error_upper_bound以及sum_other_doc_count,两者分别表示没有在这次聚合中返回、但是可能存在的潜在聚合结果以及这次聚合中没有统计到的文档数。这也很好的证明了Elasticsearch默认的对于数据查询操作仅仅只是对数据库中的部分数据进行模糊查询,而不是精确的查询数据库中的所有数据记录。为了将两者放在公平的环境下进行测试,我们需要关闭Elasticsearch的模糊查询,处理的方式是采用Elasticsearch中的scroll接口以及定义buckets的大小来对控制Elasticsearch返回全部的查询结果。

在本次测试中,使用了DolphinDB脚本完成了DolphinDB的查询性能测试。使用了Python脚本+DSL来完成Elasticsearch的查询性能测试。

我们对三张数据表进行了若干种常用的SQL查询。为了减小偶然因素对结果的影响,本次查询性能测试对每种查询操作均进行10次查询,然后对总时间取平均值,时间以毫秒为单位。各个测试数据集的测试脚本和结果如下表所示。

(1)CN_Stock表

DolphinDB中的查询脚本:

6756419a0c03a5ee21e51bd8a5cbc658.png

查询性能测试结果(数据量:5,332,932):

436170aa8845c219b5128a73fa43a220.png

(2)US_Prices表

DolphinDB中的查询脚本:

01ba3e111169beef1ef7d7a4e129598f.png

查询性能测试结果(数据量:50,591,907):

fd2f704f5f6deb9da2b65228aca4437c.png

(3)TAQ表

DolphinDB中的查询脚本:

90afe4dfa45bbc6f5fdd391726f4c350.png

查询性能测试结果(数据量:1,366,036,384):

14edd7ea437fa80c1932d70278097e00.png

对于本次的查询性能测试,我们可以得出以下结论:

(1)在同一张表的所有测试中,DolphinDB的性能都领先Elasticsearch多倍。特别的,对于简单的过滤查询,DolphinDB的性能是Elasticsearch的性能的1~2个数量级(见CN_Stock表测试结果的1~4、US_Prices表测试结果的1~4)。

(2)在有关聚合查询的测试结果中,DolphinDB的性能也都优于Elasticsearch,平均是8~9倍。特别的,按时间分组的聚合查询中,DolphinDB的性能是Elasticsearch的13~15倍(见CN_Stock表测试结果的5~10,US_Prices表测试结果的5~10)。

(3)在不同的数据规模的相同类型的查询测试中,我们可以看出随着数据规模的上升,Elasticsearch的精确查询的耗时增长幅度远大于DolphinDB,DolphinDB在不同数据规模下的稳定性优于Elasticsearch。

5.2 I/O性能测试

Elasticsearch提供了_bulk API批量写入数据的功能。在创建一条新的文档时,首先需要描述文档中可能包含的每个字段的属性,数据类型(比如 keyword, text, integer 或 date),以及这些字段是否需要被 Lucene 索引或储存。然后Elasticsearch为文档的这些属性构建相应的映射,并创建倒排索引后形成Lucene中的段。最后通过refresh以及flush机制将倒排索引存储于磁盘上。其中将内存中的倒排索引flush到磁盘上这一过程是决定Elasticsearch性能的关键。值得注意的是,虽然Elasticsearch提供了_bulk API 来批量导入数据,同时也可以设置index.refresh_interval = -1 以及index. number_of_replicas = 0 来进行导入优化。但是在大批量数据导入的情况下,当内存中的缓冲区满的时候,仍然会触发refresh,并且进行flush将数据存储到磁盘上,因此优化的效果并不是很明显,Elasticsearch数据导入缓慢是一个很显著的缺点。

DolphinDB中创建一张分布式数据表并写入数据的时候,首先根据分布式数据表的分区类型决定不同分区的数据写入的数据节点位置。在分区内部,数据是采用列式存储的方式进行组织,通过节点之间的配合来进行数据的导入与查询等操作,数据导入很快,性能极高。

下表是两者的数据导入的I/O性能测试结果,从中可以明显的观察到ES/DDB的载入耗时比随着数据量的上升而增大,特别的,当数据量为60.6GB时,Elasticsearch导入耗时12个小时以上。数据导入脚本见附录。

ea90aa95abae6e10fb01ac395454398b.png

5.3 磁盘占用空间测试

Elasticsearch以其搜索的高效性与时效性著称。它是基于Lucene而构建起来的分布式搜索引擎且对source字段的内容进行了压缩处理,但其内部是为每个创建的文档构建倒排索引,并将倒排索引存储在磁盘中的。因为在磁盘上需要对每个文档添加额外的索引信息,从而需要更大的存储空间来存放。而DolphinDB并不需要其余的索引信息,真正做到了对原数据的压缩存储。测试结果如下表所示。

2557fe46c107b1a6cc5893a859ffed43.png

5.4 内存占用

为了更加全面的观测到DolphinDB与Elasticsearch在执行过程中的内存占用情况,使用Linux命令htop来监视DolphinDB和Elasticsearch的内存占用情况(内存总大小为32GB),结果如下:

ecdf4d33bde2a2fbe0179bd3006d8f26.png

5.5 其他方面比较

(1)Elasticsearch通过需要安装插件来支持SQL语言,同时其内置的DSL语言是JSON格式,语法比较复杂。而DolphinDB内置了完整的脚本语言,不仅支持SQL语言,而且支持命令式、向量化、函数化、元编程、RPC等多种编程范式,可以轻松实现更多的功能。

(2)Elasticsearch的主要用途是提供了一个分布式多用户能力的全文搜索引擎,支持模糊查询,文档(行)不需要固定结构,不同文档可以具有不同字段集合。而DolphinDB只支持结构化数据。

(3)DolphinDB提供600余种内置函数,可满足金融领域的历史数据建模与实时流数据处理,及物联网领域中的实时监控与数据实时分析处理等不同的场景需求。提供时序数据处理需要的领先、滞后、累积窗口、滑动窗口等多种指标的函数,且在性能上进行了优化,性能极优。 因而与Elasticsearch相比,DolphinDB拥有更多的适用场景。

(4)Elasticsearch用于时序数据库中时并不支持表连接,而DolphinDB不仅支持表连接,还对asof join及window join等非同时连接方式做了优化。

(5)DolphinDB对数据写入支持分布式事务,Elasticsearch不支持事务。

6. 小结

Elasticsearch支持结构化数据和非结构化数据,支持模糊查询,精确查询,和聚合计算,适合很多应用场景。但是与DolphinDB这样专业的时序数据库相比,无论在功能上和性能上,都有很大的差距。尤其当数据量急剧膨胀,超过物理内存上限时,内存耗用高、磁盘空间占用高的缺点暴露出来,对历史数据计算时性能有明显的下降。

DolphinDB和Elasticsearch的详细配置信息、DolphinDB和Elasticsearch的测试代码以及数据导入脚本见附录