当前位置: 首页 > Linux

干货丨DolphinDB和MongoDB在时序数据上的对比测试

时间:2023-04-06 04:19:22 Linux

DolphinDB和MongoDB是为大数据而生的数据库。但是两者之间有很大的区别。前者是列式存储的多模型数据库,主要用于结构化时序数据的高速存储、查询和分析。后者是一种基于文档的NoSQL数据库,可用于处理非结构化和结构化数据,可以快速查找或写入基于键值的文档。MongoDB有自己最适合的应用场景。然而,市场上缺乏优秀的大数据产品,在物联网和金融领域,很多用户尝试使用MongoDB来存储和查询结构化时序数据。本次测试的目的是评估MongoDB是否适合这样海量的时序数据集。一、测试环境本次测试在单机上进行,测试设备配置如下:主机:DELLOptiPlex7060CPU:Intel(R)Core(TM)i7-8700CPU@3.20GHZ,6核12线程内存:32GB(8GBx4,2,666MHz)硬盘:2THDD(222MB/sread;210MB/swrite)操作系统:Ubuntu18.04LTSDolphinDB使用Linux0.89作为测试版本,所有节点的最大连接数为128,数据副本设置为2,设置1个控制节点,1个代理节点,3个数据节点。MongoDB使用Linux4.0.5社区版作为测试版本,分片集群线程数为12,所有服务器最大连接数为128。MongoDB的分片集群设置为1台配置服务器,1台mongos路由服务器,和3个分片服务器。configserver设置为1个master节点2个slave节点的replica集群,3个shardserver都设置为1个master节点,1个slave节点,1个仲裁节点的replica集群。DolphinDB和MongoDB的参数配置见附录1。2.数据集本报告测试了DolphinDB和MongoDB在小数据级别(4.2GB)和大数据级别(62.4GB)的性能。对于两种大小的数据集,我们测试两种数据库在磁盘分区情况下的性能,查询时间包括磁盘IO的时间。为了保证测试的公平性,我们在测试前通过linux命令清除了页面缓存、目录缓存和硬盘缓存:sync,echo1,2,3|tee/proc/sys/vm/drop_caches,然后依次执行13条query,记录执行时间。两个数据集的表结构和划分方式如下:Devicesensorinformationsmalldataset(CSV文件,4.2G,3000万条数据)我们选择devices_readings_big.csv(以下简称readings数据集)和TimescaleDB提供的device_info_big官网.csv(以下简称info数据集)设备传感器数据作为小数据测试集。读数数据集包含3000个设备10000个时间间隔(2016.11.15-2016.11.19)的传感器信息,包括传感器时间、设备ID、电池、内存、CPU等时序统计。info数据集包括3000台设备的设备ID、版本号、制造商、型号和操作系统等统计信息。数据来源:https://docs.timescale.com/v1.1/tutorials/other-sample-datasets数据集共有3000万条数据(4.2G),压缩包内含设备信息表以及一个设备传感器信息记录表,表结构和分区方式如下:readings数据集info数据集中的device_id字段有3000个不同的值,在readings数据集中重复出现。这种情况下使用string类型不仅占用空间大而且查询效率低,而DolphinDB的symbol类型可以很好的解决空间占用和效率两个问题。我们在DolphinDB数据库中使用组合分区。时间字段作为分区的第一维,按天分为4个区,device_id作为分区的第二维。每天共划分10个区。最后,每个分区包含的原始数据大小约为100MB。我们在MongoDB中同样使用组合分区,以时间作为分区的第一维度,根据日期进行范围分区,然后将设备ID作为第二分区维度,根据设备ID进行范围分区。MongoDB的范围分区是基于块的大小。当数据块的大小大于一定阈值时,数据库会自动将一个大数据块分成两个小数据块,实现分区。经过测试,我们发现当chunkSize(数据块分区阈值)为1024时,性能是最好的。最后,读数数据集共分为17个分区。MongoDB要求分区字段必须有索引,所以我们创建一个日期+设备ID的复合索引。复合索引可以加快查询速度,但是MongoDB在建立索引时会消耗时间和空间。读数数据集分区time_1_device_id_1增量索引构建耗时5分钟,占用空间1.1G。建立索引的脚本如下:使用device_ptdb.device_readings.createIndex({time:1,device_id:1}股票交易大数据集(CSV文件,62.4G,16亿条数据)我们选择纽约股票交易所(NYSE)提供2007.08.07-2007.08.10四天股市Level1行情数据(以下简称TAQ数据集)作为大数据测试集,该数据集包含4天交易时间内8000多只股票,股票代码,买入价,卖出价,买入量,卖出量等行情信息。数据集有4个csv文件,每个文件在14G到17G之间,总大小为62.4G,约16亿条数据,每个CSV文件保存了一个交易日的交易信息,数据来自(https://www.nyse.com/market-data/historical)。TAQ数据集的结构如下:在DolphinDB中,我们使用组合分区,以日期字段作为分区的第一维度,每天一个分区,一共四个分区,然后符号字段作为分区的第二维度,按照范围分区,分成100个每天分区。最后共分为400个分区,每个分区约40MB。在MongoDB中,也采用了组合分区的方式。分区维度与DolphinDB相同。chunkSize设置为1024,共分为385个分区。MongoDB在对TAQ数据集进行分区时创建一个date_1_symbol_1增量索引耗时53分钟,占用空间19G。创建索引的脚本如下:usetaq_pt_dbdb.taq_pt_col.createIndex({date:1,symbol:1}3.数据库导入导出性能对比3.1导入性能在DolphinDB中使用如下脚本导入:timer{for(fpinfps){job_id_tmp=fp.strReplace(".csv","")job_id_tmp1=split(job_id_tmp,"/")job_id=job_id_tmp1[6]job_name=job_idsubmitJob(job_id,job_name,loadTextEx{db,`taq,`date`symbol,fp})printnow()+":imported"+fp}getRecentJobs(size(fps))}MongoDB在导入TAQ数据集时,为了加快导入速度,将63G数据分成导入16个小文件,每个文件大小在3.5G到4.4G之间,然后使用以下脚本导入:forfin/media/xllu/aa/TAQ/mongo_split/*.csv;do/usr/bin/mongoimport-hlocalhost--port40000-dtaq_pt_db-ctaq_pt_col--typecsv--columnsHaveTypes--fields"symbol.string(),date.date(20060102),time.date(15:04:05),bid.double(),ofr.double(),bidsiz.int32(),ofrsiz.int32(),mode.int32(),ex.string(),mmid.string()"--parseGraceskipRow--numInsertionWorkers12--file$fecho"Theimportoffile$fiscomplete"done导入性能如下表所示:从上表可以看出,DolphinDB在导入结构化时序数据时比MongoDB快很多。以下是从几个方面分析导入结果(1)横向比较由于两个数据集的字段数量和字段类型不同,readings数据集多为string类型,TAQ数据集多为string类型数字类型。类型导入速度更快,因此您可以看到MongoDB和DolphinDB在导入TAQ数据集时速度更快。(2)纵向对比MongoDB是文档型数据库,导入速度受文档数量影响较大。可以看出,导入3000万条记录大约需要1个小时,导入16亿条记录大约需要55个小时。记录条数相差53倍左右,导入时间相差55倍左右。考虑到每个记录字段类型和数据的影响,可以认为导入速度与记录数大致成正比。DolphinDB是一个列式数据库。在存储这种结构化数据时,DolphinDB把一个字段看作一个列,存储在一个列文件中。导入读数数据集时,会创建12个列文件。导入TAQ数据集时,创建了10个列文件,列文件个数差不多。导入4.2G数据耗时63秒,导入62.4G数据耗时11分30秒。数据集大小相差约14.8倍,导入时间相差约11倍。考虑到列文件数量和存储类型的差异,可以认为导入时间与文件大小大致成正比。DolphinDB在导入时序结构化数据时,在列字段类型和数量相差不大的情况下,导入时间与文件大小呈正相关,符合列式存储数据库的特点。MongoDB在导入时序结构化数据时,在字段相差不大的情况下,导入时间与记录条数正相关,符合文档型存储数据库的特点。(3)MongoDB导入比较慢的原因分析DolphinDB采用列式存储,效率比MongoDB的文档存储高很多。MongoDB一条一条导入记录。当记录数较多时,MongoDB数据导入时间增加,性能下降。MongoDB配置分片集群时,必须开启journaling日志,在导入操作前先写入记录,降低了导入速度。因为MongoDB是一个NoSQL数据库,所以没有主键的概念。为了保证唯一性约束,在导入数据时,必须创建一个由数据库自动生成的唯一性索引来表示每条记录。数据导入和索引必须同时进行,这样会降低导入速度。3.2导出性能在DolphinDB中使用如下脚本导出数据:timersaveText((select*fromt),"/media/xllu/aa/device/device_readings_out.csv")在MongoDB中使用如下脚本导出数据:mongoexport-hlocalhost:40000-ddb_nopt-cdevice_readings-o/media/xllu/aa/device/device_readings_mongo_out.csv小数据集导出性能如下表所示:4.数据库磁盘空间占用与数据库磁盘空间占用性能对比comparison主要与DolphinDB对比将readings数据集和TAQ数据集用MongoDB数据库导入后,分区情况下各数据库数据占用磁盘空间的大小。磁盘使用指标是磁盘上数据的大小。DolphinDB通过读取所有列式数据文件的大小直接获取,MongoDB通过db.stats()获取数据存储的大小。两个数据库都有备份,MongoDB中的数据存储大小还包括索引的大小。测试结果如下表所示:同样的数据量,MongoDB占用的磁盘空间大约是DolphinDB的2~3倍。主要原因如下:(1)DolphinDB采用列式存储方式,每一列都有固定的类型。通过LZ4Compression算法,将每个字段按其类型压缩存储为一个列文件。并且对于符号类型,还采用了位图压缩算法来解决存储空间占用问题,进一步提高了压缩率。MongoDB在本次测试中使用了WiredTiger存储引擎和snappy压缩算法。(2)在MongoDB中创建分区数据库,需要对所有分区字段进行索引,进一步导致存储空间更大。经过分析发现readings数据集为time和device_id字段创建的索引大小为1.1G,TAQ数据集为date和symbol字段创建的索引大小为19G。5.数据库查询性能对于读数数据集和TAQ数据集,我们比较了以下8种常用的SQL查询。1、点查询:根据某个字段的具体值进行查询。2、范围查询:按照时间间隔,按照一个或多个字段的范围进行查询。3、聚合查询:根据数据库提供的聚合函数进行查询,如对字段列进行计数、平均、求和、最大值、最小值、标准差等。4、精度查询:根据不同标签维度列进行数据聚合,实现高维或低维字段范围查询,小时精度和分钟精度测试。5、关联查询:根据不同的字段,在相同精度、相同时间范围内过滤查询的基础上,过滤出具有关联性的索引列,并进行分组。6、比较查询:将表中某个字段的内容按照二维重新排列成表(第一维作为列,第二维作为行)7、抽样查询:根据数据数据库提供的采样API,可以在每次查询时手动指定采样方式进行数据稀疏处理,防止查询时间范围过大时出现数据过载的问题。8.经典查询:实际业务中常用的查询。执行时间以毫秒为单位。为了排除网络传输等不稳定因素的影响,查询性能比较的时间指标是服务器执行某次查询的时间,不包括结果传输和显示的时间。4、2G设备传感器信息小数据集查询测试对于小数据集测试,我们都是测试磁盘分区数据,执行时间包括磁盘IO时间。为保证测试的准确性和公平性,Linux系统命令sync;回声1,2,3|tee/proc/sys/vm/drop_caches用于在每次测试开始前清除系统的页面缓存、目录项缓存和硬盘缓存,启动程序后执行一次sample,并记录执行时间。在DolphinDB中使用如下脚本获取数据库句柄:dp_readings="dfs://db_range_dfs"device_readings=loadTable(dp_readings,`readings_pt)在MongoDB中执行usedevice_pt语句将数据库切换为device_pt数据库。在执行关联查询时,由于info数据集的数据量较小,可以将数据加载到内存中。在MongoDB中执行db.device_info.find({})加载所有3000条设备记录,在DolphinDB中执行loadText(dp_info)将3000条设备记录加载到内存中。使用DolphinDB中的定时器计算查询执行时间,使用MongoDB中的explain()函数获取执行时间。下面是DolphinDB在小数据集上的查询脚本,MongoDB的查询脚本在附录中。查询性能如下表所示:对于范围查询,在包含分区字段的查询中,如查询3和查询4,MongoDB可以调用复合索引,DolphinDB可以通过分区字段加速查询。在这种情况下,两个数据库的差距在4倍以内,并不是很大。在包含非分区字段的查询中,如查询5所示,MongoDB无法调用非分区字段的索引,需要对所有字段进行搜索和过滤。DolphinDB不需要搜索不在where过滤条件中的字段。在这种情况下,DolphinDB与MongoDB的差距进一步拉大。可以看出,在处理这种结构化时序数据时,DolphinDB采用的列式存储方式比MongoDB的索引方式效率更高,也更适合查询多维结构化数据。对于点查询,在查询1中,MongoDB在建立time+device_id索引的时候,可以快速找到某个时间点的记录。DolphinDB按照日期分为4天。要查找某一天的具体时间点,需要选择设置分区,然后进行搜索。在这种情况下,DolphinDB比MongoDB慢。查询2中,MongoDB的过滤字段只有设备ID,不包括时间字段。我们从explain()中发现,查询过程中并没有调用复合索引。这是因为查询字段必须包含复合索引的第一个字段,索引才会起作用。Query1只根据设备ID进行过滤,不涉及时间字段的过滤。MongoDB不会调用复合索引,所以在这种情况下,DolphinDB比MongoDB更快。对于关系查询,MongoDB作为一个NoSQL数据库,只支持左外连接,并且由于没有关系数据库的主键约束,所以实现表连接查询只能使用嵌入文档,不利于计算和聚合。作为关系型数据,DolphinDB支持等值连接、左连接、全连接、asof连接、窗口连接和交叉连接。具有丰富的表连接查询功能,可以高效便捷地处理海量结构化时序数据。从查询9~10可以看出,DolphinDB比MongoDB更快,关联查询越复杂,性能差距就越大。对于抽样查询,MongoDB可以通过聚合函数中的sample语句实现抽样查询。抽样方法取决于集合的大小、N(样本数)的大小和实现抽样查询的样本语句。抽样方法取决于集合的大小。number)和示例语句在管道中的位置。DolphinDB不支持全表采样,只支持分区字段采样。由于两次数据采样查询的实现过程差异较大,不做对比。对于插值查询,MongoDB没有内置函数实现插值查询,而DolphinDB支持4种插值方式,ffill向后非空值填充,bfill向前非空值填充,lfill线性插值,nullFill用非空值填充指定的值。对于比较查询,MongoDB是一个文档型数据库,它的存储单元是一个文档。该集合包含多个文档。文档是BSON格式,没有行和列的概念。因此,不可能选择二维将表中某个字段的内容组织成A表(第一维为列,第二维为行)。DolphinDB内置了一个pivotbyfunction语句,选择类别的维度可以方便的将指定的内容组织成一个表。因为MongoDB不支持比较查询,所以不做比较。62.4G股票交易大数据集查询测试DolphinDB使用如下脚本获取数据库句柄:taq_pt_db="dfs://db_compound_dfs"taq_pt_col=loadTable(taq_pt_db,`readings_pt)在MongoDB中执行usetaq_pt_db语句切换数据库到taq_pt_db数据库。DolphinDB对大数据集的查询脚本如下:查询性能如下表:在数据量大,两个数据库分区的情况下,DolphinDB仍然比MongoDB快5-20倍左右.查询1是基于日期和股票代码的点查询。在这种情况下,MongoDB和DolphinDB的性能差距并不大。DolphinDB比MongoDB稍慢。这是因为MongoDB在进行复合分区的时候会创建一个date+symbol的复合索引,这样可以更快。结果是MongoDB更好的应用场景,但是对比查询5到9,可以看出MongoDB的计算性能还是不行和DolphinDB一样好,差距大约是10-20倍。6.总结时序数据库DolphinDB和MongoDB在时序数据库集上的对比测试,主要结论如下:DolphinDB的数据导入速度比MongoDB高两个数量级。数据量越大,性能差距越明显。在数据导出方面,DolphinDB比MongoDB快50倍左右。在磁盘空间占用方面,MongoDB占用的磁盘空间是DolphinDB的2到3倍。在数据库查询性能方面,DolphinDB在4项查询性能测试中比MongoDB快30倍;在5项查询性能测试中比MongoDB快10-30倍;在12项查询性能测试中比MongoDB快数倍;DolphinDB仅在两点查询测试中比MongoDB慢。在处理结构化时序数据时,无论是数据导入导出、磁盘空间占用还是查询速度,DolphinDB的性能都优于MongoDB。但是,MongoDB作为一个基于文档的NoSQL数据库,在数据模型多变的场景和处理非结构化数据方面更有优势。附录1.DolphinDB环境配置controller.cfgagent.cfgcluster.cfgcluster.nodes2。MongoDB环境配置Shard服务器配置:主节点:master_shard.txt从节点:slave_shard.txt仲裁节点:arbiter_shard.txt路由服务器配置:master_mongos.txt配置服务器:主节点:master_config.txt从节点:slave_config.txt仲裁节点:arbiter_config.txt3.DolphinDB分区脚本阅读分区脚本TAQ分区脚本4.MongoDB分区脚本阅读分区脚本TAQ分区脚本5.MongoDB查询脚本阅读查询脚本TAQ查询脚本