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.csv
Quotes = 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 Quotes
saveText(Quotes_Big, DATA_DIR + "/Quotes_Big.csv")
// 产生 Quotes_Small.csv
symbols = exec distinct Symbol from Quotes
symbols = symbols[0..(symbols.size() - 1) % 4 == 3]
Quotes_Small = select * from Quotes_Big where Symbol in symbols
saveText(Quotes_Small, DATA_DIR + "/Quotes_Small.csv")
// 建设数据库和数据表
if (existsDatabase(PTNDB_DIR)) {dropDatabase(PTNDB_DIR)
}
sym = `A`D`G`J`M`P`S`V`Z
db = 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.csv
1
复制代码
DolphinDB:
saveText(Quotes, DATA_DIR + "/Quotes.csv")
1
复制代码
官网:www.dolphindb.com
邮箱:info@dolphindb.com