DolphinDBとDruidはどちらも、分散分析時系列データベースです。前者はC ++を使用して開発され、後者はJavaを使用して開発されていますが、この2つには、アーキテクチャ、機能、およびアプリケーションシナリオの点で多くの共通点があります。このレポートでは、SQLクエリ、データインポート、およびディスク容量の観点から2つのパフォーマンスを比較します。
テストデータセットは、約300GBの米国株式市場の取引および見積もりデータを使用します。テストの結果、次のことがわかりました。
- DolphinDBのデータ書き込み速度はDruidの約30倍です。
- DolphinDBのクエリ速度はDruidの約10倍です。
- DolphinDBデータベースの静的スペース使用量はDruidのそれより80%高く、実行時に使用される合計ディスクスペースはDruidのそれよりわずかに低くなっています。
1.システムの紹介
DolphinDBは、C ++で記述された分析分散時系列データベースであり、ストリーミングデータ処理エンジン、並列コンピューティングエンジン、および分散コンピューティング機能が組み込まれています。DolphinDBデータベースには、水平および垂直クラスター拡張をサポートする分散ファイルシステムが組み込まれています。SQLおよびPythonのようなスクリプト言語を提供し、SQLを使用してデータを操作できるだけでなく、より複雑なメモリ計算を完了することもできます。既存のアプリケーションとの統合を容易にするために、他の一般的に使用されるプログラミング言語でAPIを提供します。DolphinDBは、数兆のデータを迅速に処理できます。金融分野での履歴データ分析モデリングとリアルタイムストリーミングデータ処理、およびインターネットオブシングス分野での大規模なセンサーデータ処理とリアルタイム分析で優れたパフォーマンスを発揮します。
Druidは、Java言語によって実装されたOLAPデータウェアハウスであり、低遅延のクエリと数兆レベルのデータの挿入、およびリアルタイムのストリーミングデータ分析に適しています。Druidは、分散型、SNアーキテクチャ、列型ストレージ、反転インデックス、ビットマップインデックスなど、高い可用性と高いスケーラビリティを特徴とする主要なテクノロジーを採用しています。同時に、Druidは複数の言語インターフェイスを提供し、いくつかのSQLをサポートします。
2.システム構成
2.1ハードウェア構成
このテストのハードウェア構成は次のとおりです。
機器:DELL OptiPlex 7060
CPU:Inter(R)Core™i7-8700 CPU @ 3.20GHz、6コア、12スレッド
メモリ:32GB
ハードディスク:256GB SSD、1.8TB SeagateST2000DM008-2FR102メカニカルハードディスク
オペレーティングシステム:Ubuntu 16.04 x64
2.2環境構成
今回のテスト環境は、単一サーバー下のマルチノードクラスターです。DolphinDBのデータノードの数を4に設定し、単一のデータノードの使用可能な最大メモリを4GBに設定します。Druidノードの数を5に設定します。これは、オーバーロード、ブローカー、履歴、コーディネーター、およびmiddleManagerです。Druidはデフォルトでクエリ結果をキャッシュします。これは、テスト中に複数のクエリを平均化する方法の正確さに影響するため、クエリキャッシュ機能はオフになっています。Druidの書き込みパフォーマンステストに影響を与えないために、Druidのロールアップ機能は無効にされました。他のすべての構成は、デフォルト構成に従います。
元のcsvファイルはHDDに保存されます。データベースはSSDに保存されます。
3.テストデータセット
このテストでは、2007年8月の米国株式市場レベル1のTAQデータセットを使用します。TAQデータセットは毎日23個のcsvファイルに分割されます。1つのファイルのサイズは7.8Gから19.1Gの範囲です。データセット全体のサイズは約290Gで、合計6,561,693,704個のデータがあります。
DolphinDBおよびDruidのテストデータセットTAQの各フィールドのデータタイプは次のとおりです。
Druidでは、DATEフィールドはタイムスタンプ列として指定されます。他のフィールドはディメンションフィールドとして使用されます。
4.データパーティションスキーム
DolphinDBでは、ストックコードと日付の組み合わせパーティションが採用されており、ストックコードの範囲に応じて128パーティション、日付に応じて23パーティションに分割されています。
Druidは時間範囲パーティションのみをサポートするため、DATE列をタイムスタンプタイプとして指定し、日を単位として23のパーティションに分割します。
5.比較テスト
DolphinDBとDruidを、データベースクエリのパフォーマンス、I / Oパフォーマンス、およびディスクフットプリントの観点から比較しました。
5.1データベースクエリのパフォーマンス
DolphinDBスクリプト言語はSQL構文をサポートし、時系列データの関数を拡張しました。Druidは、クエリ用のJsonデータ形式に基づく言語を提供し、SQLクエリ用のdsqlも提供します。このテストでは、Druid独自のdsqlを使用します。
TAQデータセットに対していくつかの一般的なSQLクエリを実行しました。偶発的な要因による結果への影響を減らすために、このクエリパフォーマンステストはクエリ操作ごとに10回実行され、合計時間が平均化され、時間はミリ秒単位でした。DolphinDBをテストするとき、timerステートメントを使用してサーバー上のSQLステートメントの実行時間を評価しました。Druidはクエリ時間を出力するツールまたは関数を提供しないため、クライアントコマンドラインツールdsqlによって出力される実行時間が使用されます。DolphinDBと比較して、Druidによって返される実行時間は、クエリ結果の送信時間と表示時間が長くなります。クエリによって返されるデータの量が非常に少ないため、dsqlとDruidサーバーは同じノード上にあり、影響時間は約1ミリ秒です。1ms前後の時間はテストの結論に影響を与えないため、特別な処理は行われません。
次の表に、7つのクエリのSQL表現を示します。
試験結果を下表に示します。
結果から、ほとんどすべてのクエリで、DolphinDBのパフォーマンスはDruidよりも優れており、速度はDruidの約3〜30倍であることがわかります。
Druidではタイムスタンプに基づくセグメンテーションのみが許可されていますが、DolphinDBではデータを複数のディメンションから分割できますが、TAQの分割には時間とストックコードの2つのディメンションが使用されるため、クエリはストックコードに従ってフィルタリングまたはグループ化する必要があります。 DolphinDBの利点は、テスト(テスト1、3、6、7など)でより明白になります。
5.2 I / Oパフォーマンステスト
単一のファイル(7.8G)と複数のファイル(290.8G)をインポートするときの、DolphinDBとDruidのパフォーマンスをテストしました。公平を期すために、Druidのロールアップ機能をオフにしました。テスト結果を下の表に示します。時間は秒単位です。
同じ状況で、単一のファイルをインポートする場合、Druidのインポート時間はDolphinDBの16倍以上になります。複数のファイルをインポートする場合、DolphinDBは並列インポートをサポートするため、速度はDruidよりも高速です。データインポートスクリプトについては、付録2を参照してください。
5.3ディスク容量テスト
DolphinDBとDruidにデータをインポートした後、2つのデータ圧縮率を比較しました。試験結果を下表に示します。
DolphinDBは、LZ4圧縮アルゴリズムを使用して、列に格納されているデータをすばやく圧縮します。圧縮する前に、DolphinDBのSYMBOLタイプは、辞書エンコーディングを使用して文字列を整数に変換します。データストレージのプロセスでは、DruidはLZ4アルゴリズムを使用してタイムスタンプとメトリックを直接圧縮し、辞書エンコーディング、ビットマップインデックス、および轟音ビットビットを使用してディメンションフィールドを圧縮します。ディクショナリエンコーディングを使用すると、文字列のストレージスペースを削減でき、ビットマップインデックスはビット単位の論理操作をすばやく実行でき、ビットマップインデックスの圧縮によりストレージスペースをさらに節約できます。
このテストでは、DolphinDBデータベースが占めるディスク容量はDruidのディスク容量よりも約80%高くなっています。この違いを引き起こす主な要因は、BIDとOFRの2つの浮動小数点フィールドの圧縮率がDolphinDBとDruidでかなり異なることです。DolphinDBでは、これら2つのフィールドの圧縮率は20%ですが、Druidでは5%にもなります。その理由は、テストデータセットが履歴データセットであり、データが日付と在庫の2つのフィールドに従ってソートされているためです。短期間の株式の価格変動は非常に小さく、一意の見積もりの数は非常に限られており、Druidはビットマップ圧縮を使用して非常に良い結果を達成しています。
Druidデータベースの圧縮率は高く、静的ディスクスペースは小さくなりますが、Druidの実行時にセグメントキャッシュディレクトリが生成され、合計ディスクスペース占有量は65GBに達します。DolphinDBの実行時に追加のスペースは必要ありませんが、合計ディスクスペースはDruidよりもわずかに小さくなっています。
6.まとめ
DolphinDB for Druidのパフォーマンス上の利点は、(1)ストレージメカニズムとパーティショニングメカニズムの違い、(2)開発言語(c ++とjava)の違い、(3)メモリ管理の違い、(3)などの多くの側面からもたらされます。 4)アルゴリズム(並べ替えやハッシュなど)の実装の違い。
パーティショニングに関して、Druidは時間タイプのレンジパーティショニングのみをサポートします。これは、値のパーティショニング、レンジパーティショニング、ハッシュパーティショニング、およびリストパーティショニングをサポートするDolphinDBと比較して柔軟性に欠け、各テーブルは複数のフィールドに基づいてパーティショニングできます。DolphinDBのパーティションの細かさは細かく、データやクエリを特定のノードに集中させるのは簡単ではありません。DolphinDBは、クエリ中にスキャンする必要のあるデータブロックが少なく、応答時間が短く、パフォーマンスが向上します。
パフォーマンスを除いて、DolphinDBはDruidよりも機能的です。SQLサポートに関して、DolphinDBは非常に強力なウィンドウ関数メカニズムをサポートし、SQL結合をより包括的にサポートします。スライド機能、asof結合、ウィンドウ結合、および時系列データに固有のDolphinDBを適切にサポートします。DolphinDBは、データベース、プログラミング言語、分散コンピューティングを統合します。通常のデータベースクエリ機能に加えて、DolphinDBは、より複雑なメモリコンピューティング、分散コンピューティング、およびストリームコンピューティングもサポートします。
DolphinDBとDruidも動作モードが少し異なります。Druidがクラッシュするか、セグメントキャッシュをクリアした後に再起動した後、データをリロードし、各セグメントをセグメントキャッシュに解凍してからクエリを実行するのに時間がかかり、効率が低く、キャッシュがより大きなスペースを占有します。 、したがって、Druidは再起動時に長時間待機する必要があり、より多くのスペースが必要になります。
付録
付録1.環境構成
(1)DolphinDB構成
controller.cfg
localSite = localhost:9919:ctl9919 localExecutors = 3 maxConnections = 128 maxMemSize = 4 webWorkerNum = 4 workerNum = 4 dfsReplicationFactor = 1 dfsReplicaReliabilityLevel = 0 enableDFS = 1 enableHTTPS = 0
cluster.nodes
localSite、mode localhost:9910:agent、agent localhost:9921:DFS_NODE1、datanode localhost:9922:DFS_NODE2、datanode localhost:9923:DFS_NODE3、datanode localhost:9924:DFS_NODE4、datanode
cluster.cfg
maxConnection = 128 workerNum = 8 localExecutors = 7 webWorkerNum = 2 maxMemSize = 4
agent.cfg
workerNum = 3 localExecutors = 2 maxMemSize = 4 localSite = localhost:9910:agent controllerSite = localhost:9919:ctl9919
(2)ドルイド構成
_一般
#飼育係 druid.zk.service.host = zk.host.ip druid.zk.paths.base = /ドルイド #メタデータ記憶 druid.metadata.storage.type = MySQLの druid.metadata.storage.connector.connectURI = JDBC:MySQLの://db.example.com:3306 / druid #ディープストレージ druid.storage.type = local druid.storage.storageDirectory = var / druid / segments #インデックスサービスログ druid.indexer.logs.type = filedruid.indexer。 logs.directory = var / druid / indexing-logs
ブローカ:
Xms24g Xmx24g XX:MaxDirectMemorySize = 4096m #HTTPサーバースレッド druid.broker.http.numConnections = 5 druid.server.http.numThreads = 25 #スレッドとバッファーの処理 druid.processing.buffer.sizeBytes = 2147483648 druid.processing.numThreads = 7 #クエリキャッシュ druid.broker.cache.useCache = false druid.broker.cache.populateCache = false コーディネーター: Xms3g Xmx3g 履歴: Xms8g Xmx8g #HTTPサーバースレッド druid.server.http.numThreads = 25 #スレッドとバッファーの処理 druid.processing .buffer.sizeBytes = 2147483648 druid.processing.numThreads = 7 #セグメントストレージ druid.segmentCache.locations = [{"path": "var / druid / segment-cache"、 "maxSize":0}] druid.server.maxSize = 130000000000 druid.historical.cache.useCache = falsedruid 。 history.cache.populateCache = false middleManager: Xms64m Xmx64m #middleManagerあたりのタスク数 druid.worker.capacity = 3 #HTTPサーバースレッド druid.server.http.numThreads = 25 #Peonsdruid.indexer.forkでスレッドとバッファーを処理しています 。 property.druid.processing.buffer.sizeBytes = 4147483648 druid.indexer.fork.property.druid.processing.numThreads = 2
過負荷:
Xms3g Xmx3g
付録2.データインポートスクリプト
DolphinDBデータベース脚本:
if(existsDatabase( "dfs:// TAQ")) dropDatabase( "dfs:// TAQ") db = database( "/ Druid / table"、SEQ、4) t = loadTextEx(db、 'table' 、、 " /data/data/TAQ/TAQ20070801.csv ") t = select count(*)as ct from t group by symbolbuckets = cutPoints(exec symbol from t、128) buckets [size(buckets)-1] =` ZZZZZ t1 = table(buckets asbucket )t1.saveText( "/ data / data / TAQ /buckets.txt")db1 = database(" "、VALUE、2007.08.01..2007.09.01) partition = loadText(" / data / data /buckets.txt ") partitions = exec *パーティションから db2 = database(" "、RANGE、partitions) db = database(" dfs:// TAQ "、HIER、[db1、db2]) db.createPartitionedTable(table(100:0、 `symbol`date`time`bid`ofr`bidsiz`ofrsiz`mode`ex`mmid、[SYMBOL、DATE、SECOND、DOUBLE、DOUBLE、INT、INT、INT、CHAR、 SYMBOL])、 `quotes、` date`symbol) def loadJob(){ filenames = exec filename from files( '/ data / data / TAQ') db = database( "dfs:// TAQ") filedir = '/ data / data / TAQ'for (fname in filenames){ jobId = fname.strReplace( "。csv"、 "") jobName = jobId submitJob(jobId、jobName、loadTextEx {db、 "quotes"、 `date`symbol、filedir + ' / '+ fname}) } } loadJob() select * from getRecentJobs() TAQ = loadTable( "dfs:// TAQ"、 "quotes");
Druidスクリプト:
{ "type": "index"、 "spec":{ "dataSchema":{ "dataSource": "TAQ"、 "parser":{ "type": "string"、 "parseSpec":{ "format": " csv "、 " dimensionsSpec ":{ " dimensions ":[ " TIME "、 " SYMBOL "、 {" name ":" BID "、" type ":" double "}、 {" name ":" OFR "、" type ":" double "}、 {" name ":" BIDSIZ "、" type ":" int "}、 {" name ":" OFRSIZ "、" type ":" int "}、 "MODE "、 " EX "、 " MMID " ] }、 " timestampSpec ":{ " column ":" DATE "、 " format ":" yyyyMMdd " }、 " columns ":[" SYMBOL "、 " DATE "、 " TIME "、 " BID "、 " OFR "、 "BIDSIZ"、 "OFRSIZ"、 "MODE"、 "EX"、 "MMID"] } }、 "metricsSpec":[]、 "granularitySpec":{ "type": "uniform"、 "segmentGranularity": "day" 、 "queryGranularity": "none"、 "intervals":["2007-08-01 / 2007-09-01"] 、 "rollup": false } }、"ioConfig":{ "type": "index"、 "firehose":{ "type": "local"、 "baseDir": "/ data / data /"、 "filter": "TAQ.csv" }、 "appendToExisting":false }、 "tuningConfig":{ "type": "index"、 "forceExtendableShardSpecs":true " targetPartitionSize ":5000000、 "maxRowsInMemory":25000、 } } }