InfluxDB是一款开源的时序数据库,由Go语言实现。它的外围是一款定制的存储引擎TSM Tree,对工夫序列数据做了优化,优先思考插入和查问数据的性能。InfluxDB应用类SQL的查询语言InfluxQL,并提供开箱即用的工夫序列数学和统计函数。实用于监控、实时剖析、物联网、传感器数据等利用场景。是目前最为风行的工夫序列数据库。

DolphinDB Database 是一款剖析型的分布式时序数据库,内置解决流式数据处理引擎,具备内置的并行和分布式计算的性能,并提供分布式文件系统,反对集群扩大。DolphinDB以C++编写,响应速度极快。提供相似于Python的脚本语言对数据进行操作,反对类规范SQL的语法。提供其它罕用编程语言的API,不便与已有应用程序集成。在金融畛域中的历史数据分析建模与实时流数据处理,以及物联网畛域中的海量传感器数据处理与实时剖析等场景中表现出色。

本文将会对DolphinDB和InfluxDB进行性能测试比照。

在本次测试中,硬件配置如下:

设施:DellXPS 8920(07DC)

CPU:Inter® Core™ i7-7700 CPU @ 3.60GHz,4外围8线程

内存:16GB

硬盘:512GB SSD

操作系统:Ubuntu 16.04 x64

因为InfluxDB集群版本闭源,所以本次测试应用的DolphinDB和InfluxDB均为单机版本,并且所有配置项都是默认配置。

1. 数据生成

在本次测试中,咱们应用NYSE网站上2016年10月24日纽约交易所的股票交易数据生成了Quotes_Big表。在其中取出一部分数据形成Quotes_Small表。表构造如表1所示。Quotes_Big表中的数据量为78,721,394条,Quotes_Small表中数据量为18,314,172条。数据下载链接及预处理脚本详见附录1。

因为DolphinDB和InfluxDB在存储形式上的差别,咱们采纳如下设计:咱们将Time列指定为InfluxDB中的timestamp;将Exchange和Symbol列指定为InfluxDB中的Tag列(相似于带索引的列);将Bid_Price、Bid_Size、Offer_Price、Offer_Size指定为InfluxDB中的field列(相似于无索引的列)。在DolphinD零碎中依据Symbol列在磁盘上进行RANGE分区,分为8个区。


表1.数据类型映射

2. 数据库查问性能测试

咱们对11种罕用的SQL查问进行了比照测试。Quotes_Small表的测试后果如下表2所示,Quotes_Big表的测试后果如下表3所示。其中,对于DolphinDB的测试,咱们应用的是DolphinDB官网的GUI;对于InfluxDB,咱们应用的是官网的PythonAPI。执行工夫以毫秒为单位,只蕴含查问自身执行的工夫,而不蕴含结果显示的工夫。为了缩小非凡值的影响,每个查问都执行10次,表中数据是10次查问的总工夫。本次测试的脚本详见附录2。

在简直所有测试中,DolphinDB的性能都当先InfluxDB多倍,某些状况下的差距甚至超过2个数量级(100倍)。InfluxDB惟一当先DolphinDB的测试是在Quotes_Small表的第4个测试项目,这是因为在数据量较小时DolphinDB并行搜寻分区的劣势不显著。然而,在数据量较大的状况下,DolphinDB在所有测试中上的性能都优于InfluxDB。DolphinDB能够并行搜寻分区的设计使它在过滤性查问方面的性能全面超过了InfluxDB。


表2. Quotes_Small表查问性能测试后果(数据量:18,314,172)

表3. Quotes_Big表查问性能测试后果(数据量:78,721,394)

3. 内置函数计算性能测试

咱们测试了8个DolphinDB和InfluxDB都提供的罕用内置函数。在所有测试中,DolphinDB的性能都优于InfluxDB 1-2个数量级(10-100倍)。因为DolphinDB对带有滑动窗口的函数极好的优化,在“滑动平均值”(moving_average)的测试中,DolphinDB的性能当先InfluxDB 3个数量级以上(>1000倍)。

InfluxDB在Quotes_Big表的标准差与滑动平均值计算中在长时间的卡顿后引发内存不足的问题(out of memory),阐明InfluxDB对大规模数据的简单剖析场景反对有余。而DolphinDB在此方面性能优异。

Quotes_Small表的测试后果如下表4所示,Quotes_Big表的测试后果如下表5所示。表格中数据的单位为毫秒。为缩小非凡值的影响,每个计算都进行了10次。测试脚本详见附录3。


表4. Quotes_Small表计算性能测试后果(数据量:18,314,172)

表5. Quotes_Big表计算性能测试后果

4. I/O性能测试

咱们别离应用了DolphinDB和InfluxDB的JavaAPI,比拟了在雷同的环境下写入雷同规模的数据所须要的工夫。因为InfluxDB的默认batch-size为5000,因而在两个数据库的写入程序中,单次插入的数据量都为5000条。咱们还比拟了从数据库中导出同样规模的csv文件所须要的工夫。比拟的后果是,无论是写入性能还是导出性能,DolphinDB都优于InfluxDB。对于雷同规模的18,314,172条数据,DolphinDB的导出速度为InfluxDB的2倍;写入速度约为12倍。

测试后果如下表6所示。表格中的数据单位为毫秒。测试代码详见附录4。

表6. I/O性能测试后果

5. 磁盘占用空间测试

咱们比拟了Quotes_Small表和Quotes_Big表由DolphinDB和InfluxDB存储后在磁盘上的占用空间。结果显示,两款数据库都对数据进行了压缩存储,压缩率大抵处于同一个数量级,都在20%-30%之间,但DolphinDB的压缩成果更佳。

测试后果如下表7所示,表中的数据单位为MB。

表7. 磁盘占用空间测试后果

6.其余方面比照

DolphinDB除了在基准测试中体现出优越的性能之外,还具备如下劣势:

(1)InfluxDB通过InfluxQL来操作数据库,这是一品种SQL语言;而DolphinDB内置了残缺的脚本语言,不仅反对SQL语言,而且反对命令式、向量化、函数化、元编程、RPC等多种编程范式,能够轻松实现更多的性能。

(2)InfluxDB对于特定文件格式数据例如csv文件的批量导入没有很好的官网反对。用户只能通过开源第三方工具或本人实现文件的读取,规整为InfluxDB指定的输出格局,再通过API进行批量导入。单次只能导入5000行,不仅操作简单,效率也极其低下。与之比照,DolphinDB的脚本语言中提供了loadText、loadTextEx函数,用户能够在脚本中间接导入txt或csv文件,而且效率更高,对用户更敌对。

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

(4)InfluxDB不反对表连贯,而DolphinDB不仅反对表连贯,还对asof join及window join等非同时连贯形式做了优化。

(5)InfluxDB中,对工夫序列的分组(GroupBy)最大单位是星期(week);而DolphinDB反对对所有内置工夫类型的分组,最大单位为月(month)。因而在工夫序列这一个性上,DolphinDB也有更好的反对。

(6)DolphinDB采纳分布式表引擎时反对事务,而且在一个分区的多个正本写入时,保障强一致性。

附录

附录1. 数据下载即预处理脚本

数据下载链接:nyxdata

在DolphinDB中进行数据预处理,预处理脚本如下:

DATA_DIR = "…"PTNDB_DIR = DATA_DIR + "/NYSETAQSeq"    db = database(PTNDB_DIR, SEQ, 16)def convertTime(x) {    return string(nanotimestamp(2016.10.24).temporalAdd(x/1000, "us")).substr(0, 26)}// 产生 Quotes_Big.csvQuotes = loadTextEx(db, `Quotes, , DATA_DIR+"/EQY_US_ALL_NBBO_20161024", '|')Quotes_Big = select convertTime(time) as Time, Exchange, Symbol, Bid_Price, Bid_Size, Offer_Price, Offer_Size from QuotessaveText(Quotes_Big, DATA_DIR + "/Quotes_Big.csv")//产生Quotes_Small.csvsymbols = exec distinct Symbol from Quotessymbols = symbols[0..(symbols.size() - 1) % 4 == 3]Quotes_Small = select * from Quotes_Big where Symbol in symbolssaveText(Quotes_Small, DATA_DIR + "/Quotes_Small.csv")// 建设数据库和数据表if (existsDatabase(PTNDB_DIR)) {dropDatabase(PTNDB_DIR)}sym = `A`D`G`J`M`P`S`V`Zdb = database(PTNDB_DIR, RANGE_PTN, symbol(sym))// Quotes_Big表Quotes = loadTextEx(db, `Quotes, `Symbol, DATA_DIR + “/Quotes_Big.csv”)// Quotes_Small表Quotes = loadTextEx(db, `Quotes, `Symbol, DATA_DIR + “/Quotes_Small.csv”)12345678910111213141516171819202122232425262728293031复制代码

附录2. 数据库查问性能脚本


表8. InfluxDB查问性能测试用例


表9. DolphinDB查问性能测试用例

附录3. 内置函数计算性能测试用例

表10. InfluxDB内置函数计算性能测试用例

表11. DolphinDB内置函数计算性能测试用例

附录4. I/O性能测试程序

InfluxDBInsert.java:

package test;import org.influxdb.InfluxDB;import org.influxdb.InfluxDBFactory;import org.influxdb.dto.BatchPoints;import org.influxdb.dto.Point;import java.util.concurrent.TimeUnit;public class InfluxDBInsert {    public static void main(String[] args) {InfluxDBinfluxDB = InfluxDBFactory.connect("http://192.168.1.30:8086");        int size = 18314172;        long timer = 0;        for (int i = 0; i< size; i++) {BatchPoints.BuilderbatchBuilder = BatchPoints.database("TEST");            for (int j = 0; j < 5000 &&i< size; i++, j++) {Point.Builder builder = Point.measurement("TEST").time(System.nanoTime(), TimeUnit.NANOSECONDS).addField("Bid_Price", 2.5).addField("Bid_Size", 5).addField("Offer_Price", 12.5).addField("Offer_Size", 6).tag("Exchange", "B").tag("Sym", "ALV");batchBuilder.point(builder.build());            }            long start = System.currentTimeMillis();influxDB.write(batchBuilder.build());            timer += (System.currentTimeMillis() - start);        }System.out.println(timer);    }}123456789101112131415161718192021222324252627282930313233复制代码

DolphinDBInsert.java:

package test;import com.xxdb.DBConnection;import com.xxdb.data.*;import java.io.IOException;import java.util.HashMap;import java.util.Map;public class DolphinDBInsert {    public static void main(String[] args) throws IOException {        int size = 18314172;        int range = 5000;BasicLongVector Time = new BasicNanoTimestampVector(range);BasicDoubleVectorBid_Price = new BasicDoubleVector(range);BasicIntVectorBid_Size = new BasicIntVector(range);BasicDoubleVectorOffer_Price = new BasicDoubleVector(range);BasicIntVectorOffer_Size = new BasicIntVector(range);BasicStringVector Exchange = new BasicStringVector(range);BasicStringVector Symbol = new BasicStringVector(range);DBConnection conn = new DBConnection();conn.connect("192.168.1.30", 8888);conn.run("if(existsDatabase('/home/psui/Desktop/workspace/data/NYSETAQ')){dropDatabase('/home/psui/Desktop/workspace/data/NYSETAQ')}");conn.run("db = database('/home/psui/Desktop/workspace/data/NYSETAQ', RANGE, 0 5 10)");conn.run("tbl = table(200:0,`Time`Bid_Price`Bid_Size`Offer_Price`Offer_Size`Exchange`Symbol,[NANOTIMESTAMP,DOUBLE,INT,DOUBLE,INT,STRING,SYMBOL])");conn.run("Quotes = db.createPartitionedTable(tbl,'Quotes','Bid_Size')");        long timer = 0;        for (int i = 0; i< size; i++) {            if (i + 5000 >= size) {                Time = new BasicNanoTimestampVector(size-i);Bid_Price = new BasicDoubleVector(size-i);Bid_Size = new BasicIntVector(size-i);Offer_Price = new BasicDoubleVector(size-i);Offer_Size = new BasicIntVector(size-i);                Exchange = new BasicStringVector(size-i);                Symbol = new BasicStringVector(size-i);                for (int j = 0; i< size; i++, j++) {Time.setLong(j, System.nanoTime());Bid_Price.setDouble(j, 2.4);Bid_Size.setInt(j, 6);Offer_Price.setDouble(j, 3.5);Offer_Size.setInt(j, 2);Exchange.setString(j, "A");Symbol.setString(j, "ALV");                }            } else {                for (int j = 0; j < 5000 &&i< size; i++, j++) {Time.setLong(j, System.nanoTime());Bid_Price.setDouble(j, 2.4);Bid_Size.setInt(j, 6);Offer_Price.setDouble(j, 3.5);Offer_Size.setInt(j, 2);Exchange.setString(j, "A");Symbol.setString(j, "ALV");                }            }            Map<String, Entity> map = new HashMap<>();map.put("Time", Time);map.put("Bid_Price", Bid_Price);map.put("Bid_Size", Bid_Size);map.put("Offer_Price", Offer_Price);map.put("Offer_Size", Offer_Size);map.put("Exchange", Exchange);map.put("Symbol", Symbol);            long start = System.currentTimeMillis();conn.upload(map);conn.run("tbl = table(Time,Bid_Price,Bid_Size,Offer_Price,Offer_Size,Exchange,Symbol)");conn.run("Quotes.append!(tbl)");            timer += (System.currentTimeMillis()-start);        }conn.run("Quotee = loadTable(db, 'Quotes')");        Entity entity = conn.run("select count(*) from Quotee");System.out.println(entity.getString());System.out.println(timer);    }}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778复制代码

数据导出:

InfluxDB:

$ influx -host localhost -database Quotes -format csv -execute “” >Quotes.csv1复制代码

DolphinDB:

saveText(Quotes, DATA_DIR + "/Quotes.csv")1复制代码

官网:www.dolphindb.com

邮箱:info@dolphindb.com