GridDB v5.6のリリースに伴い、この新しいアップデートに搭載された新機能について見ていきたいと思います。リリースノート全体については、GitHubから直接ご覧いただけます:GridDB CE v5.6 リリースノート。また、v5.6の新しいアップデートを含むGridDBの詳しいドキュメントは、こちらでご覧いただけます:https://www.toshiba-sol.co.jp/en/pro/griddb/docs-en/v5_6/GridDB_FeaturesReference.html https://www.toshiba-sol.co.jp/en/pro/griddb/docs-en/v5_6/GridDB_FeaturesReference.html
新機能のうち、本日ご紹介するのは、gs_node.json 構成ファイルで選択可能になった新しいデータ圧縮アルゴリズムと、GridDB CLI ツールからの自動時間集計機能です。v5.6以前では、選択可能な圧縮方法は NO_COMPRESSION と COMPRESSION_ZLIB の2つだけでした。 すべてのバージョンでデフォルト設定は依然として圧縮なしですが、バージョン5.6では、COMPRESSION_ZSTDと呼ばれる新しい圧縮方法が提供されています。
この圧縮方式は、通常のデータ圧縮やデータ自体の圧縮において、より効率的であることが期待されます。つまり、フットプリントが小さくなることが期待できます。そこで、今回はGridDBに一定量のデータを挿入し、使用されるストレージ容量を比較し、最後に3つの圧縮方式を比較します。
自動集計については、記事の最後に簡単なデモを紹介します。まずは圧縮です。
方法
前述の通り、同一のデータセットを使用してGridDBの3つのインスタンスを簡単に比較する必要があります。これを実現するには、新しいインスタンスを簡単に起動または停止でき、各インスタンスの圧縮方法を変更できるdockerが最も簡単な方法と思われます。これを行う場合、各インスタンスに同じデータセットまたは同じデータ生成スクリプトを使用します。
圧縮アルゴリズムの違いを実際にテストするのに十分な強固なデータセットを得るために、1億行のデータを使用することにしました。具体的には、圧縮がその役割を果たせるように、また、その効果を効果的に測定できるように、いくつかの点でデータセットが十分に類似していることを望みました。
3つのDockerコンテナは、griddb-server1、griddb-server2、およびgriddb-server3となります。圧縮レベルはdocker-composeファイルで設定されていますが、私にとって最も理にかなった方法で設定します。server1は「NO_COMPRESSION」、server2は旧圧縮システム(「COMPRESSION_ZLIB」)、server3は新圧縮システム(「COMPRESSION_ZSTD」)です。
そのため、生成スクリプトを実行する際には、コマンドライン引数を使用して対象としたいコンテナを指定することができます。これについては次のセクションで詳しく説明します。
進め方
この方法を実際に試しながら読んでいく場合は、GitHubページからソースコードを入手してください:。
リポジトリを取得できたら、GridDBサーバーを起動します。生成データスクリプトを実行して、1億行のデータをサーバーにプッシュする方法については、次のセクションで説明します。
3つのサーバーを起動するには、プロジェクトリポジトリのルートにあるDockerコンポジションファイルに手順が記載されています。次のように実行します。
$ docker compose build
$ docker compose up -d
すべてがうまくいけば、griddb-server1、griddb-server2、およびgriddb-server3の3つのGridDBコンテナが実行されているはずです。
実装
実装には、1億行のランダムなデータを生成するnode.jsスクリプトを使用しました。GridDBコンテナはDockerを使用して起動するため、GridDB用の3つのDockerコンテナをDockerコンポジションファイル内の個別のサービスとして作成しました。次に、そのDockerネットワーク名を取得し、nodejsスクリプトを実行する際に使用しました。
つまり、nodejsスクリプトもDockerコンテナに組み込まれ、以下のコマンドでGridDBコンテナにデータをプッシュするために使用されました。
$ docker build -t gen-data .
$ docker run --network docker-griddb_default gen griddb-server1:10001
$ docker run --network docker-griddb_default gen griddb-server2:10001
$ docker run --network docker-griddb_default gen griddb-server3:10001
以下が、そのノードJSスクリプトの全文です。
const griddb = require('griddb-node-api');
const process = require('process');
var fs = require('fs');
var factory = griddb.StoreFactory.getInstance();
var store = factory.getStore({
"notificationMember": process.argv[2],
"clusterName": "myCluster",
"username": "admin",
"password": "admin"
});
const conInfo = new griddb.ContainerInfo({
'name': "compressionBlog",
'columnInfoList': [
["timestamp", griddb.Type.TIMESTAMP],
["location", griddb.Type.STRING],
["data", griddb.Type.FLOAT],
["temperature", griddb.Type.FLOAT],
],
'type': griddb.ContainerType.COLLECTION, 'rowKey': false
});
function getRandomFloat(min, max) {
return Math.random() * (max - min) + min;
}
const putCont = async (sensorCount, data, temperature) => {
const rows = generateSensors(sensorCount, data, temperature);
try {
const cont = await store.putContainer(conInfo)
await cont.multiPut(rows);
} catch (error) {
console.log("error: ", error)
}
}
const generateSensors = (sensorCount, data, temperature) => {
const arr = []
let now = new Date();
for (let i = 1; i <= sensorCount; i++) {
let tmp = [];
let newTime = now.setMilliseconds(now.getMinutes() + i)
tmp.push(newTime)
tmp.push("A1")
tmp.push(data)
tmp.push(temperature)
arr.push(tmp)
}
return arr;
}
const AMTROWS = 10000;
const AMTPASSES = 10000;
(async () => {
try {
console.log("attempting to gen data and push to GridDB")
for (let i = 0; i < AMTPASSES; i++) {
const data = parseFloat(getRandomFloat(1, 10).toFixed(2))
const temperature = parseFloat(getRandomFloat(60, 130).toFixed(2))
await putCont(AMTROWS, data, temperature);
}
console.log("Finished pushing data!")
} catch (error) {
console.log("Error putting to container", error);
}
})();
コード自体はシンプルで説明不要ですが、もし実際に実行する場合は、この行をGridDBに挿入するには時間がかかり、サーバーのハードウェアによっては10~20分ほどかかることを覚悟しておく必要があります。
圧縮方法の結果
GridDBコンテナ内にデータ行が格納されたので、GridDBに実際のデータの圧縮を実行させます。このプロセスは自動的にバックグラウンドで実行されます。詳細は、こちらを参照してください:https://www.toshiba-sol.co.jp/en/pro/griddb/docs-en/v5_6/GridDB_FeaturesReference.html#database-compressionrelease-function。
1億行のデータがどの程度の容量を占めているかを確認するには、GridDBの各Dockerコンテナに対して次のコマンドを実行します。
$ docker exec griddb-server1 du -sh /var/lib/gridstore
16G /var/lib/gridstore/
GridDBが使用するストレージ容量の合計(スワップファイルやログを含む)を確認します。データのみを確認したい場合:
$ docker exec griddb-server1 du -sh /var/lib/gridstore/data
12G /var/lib/gridstore/data/
もちろん、これは3つのコンテナすべてに対して繰り返す必要があります。
GridDBコンテナの圧縮方式は、次のようにして確認することもできます。
$ docker exec griddb-server3 cat /var/lib/gridstore/conf/gs_node.json | grep "storeCompressionMode"
"storeCompressionMode": "COMPRESSION_ZSTD",
使用されたストレージ容量のテストに加え、データのロードに要した時間とクエリに要した時間をテストしました。結果は以下の表でご覧いただけます。各行/セルにおいて、値が低いほど優れており、優れたユーザーエクスペリエンスとユーザビリティを示しています。
| NO_COMPRESSION | COMPRESSION_ZLIB | COMPRESSION_ZSTD (added v5.6) | |
| Search (ms) | 32,644 | 20,666 | 11,475 |
| Agreggation (ms) | 30,261 | 13,302 | 8,402 |
| Storage (gridstore) | 11,968,312 (17GB) | 7,162,824 (6.9GB) | 6,519,520 (6.3GB) |
| Storage (/data) | 17,568,708 (12GB) | 1,141,152 (1.1GB) | 1,140,384 (1.1GB) |
| Insert (m:ss.mmm) | 14:42.452 | 15:02.748 | 15:05.404 |
クエリの速度をテストするために、select * と集約クエリである select AVG(data) from の両方を実行し、3つの結果の平均を計算してテーブルに格納しました。
結果は明らかです。圧縮は弊害よりもメリットの方がはるかに大きいのです。ストレージ容量の節約になるだけでなく、クエリの速度も向上します。バージョン5.6の圧縮方法は、ストレージ容量を節約し、クエリの速度も大幅に向上させるようです。もちろん、これらはすべてコンシューマーレベルのハードウェア上で実行されます。
CLIによる自動集約
この機能は、Linux マシン上の cron を利用して、作成したスクリプトを定期的に実行します。しかし、本質的には、この追加機能により、コンテナの1つで集約を実行し、それらの値をすべて別のテーブルにプッシュすることが可能になります。これにより、リソースが使用されていないときにバックグラウンドで定期的に新しいクエリを実行できるようになります。この方法では、集約を実行して、おそらくは長時間を要する計算を待つ必要がなく、更新された/新しい値を入手することができます。
その仕組みは次の通りです。あるテーブルの値を別のテーブルに挿入することができます。
gs[public]> INSERT OR REPLACE INTO device_output (ts, co) SELECT ts,avg(co) FROM device WHERE ts BETWEEN TIMESTAMP('2020-07-12T00:29:38.905Z') AND TIMESTAMP('2020-07-19T23:58:25.634Z') GROUP BY RANGE(ts) EVERY (20,SECOND);
The 34,468 records had been inserted.
このことを理解していれば、GridDB CLI スクリプトファイル(.gsh)を作成し、そのスクリプトでテーブルから最新の値を取得し、集約を実行し、etl_outputファイルに書き出す、といった賢いことが可能です。このスクリプトファイルを作成すると、cronジョブを設定して、スクリプトをバックグラウンドで定期的に実行するようスケジュールすることができます。このプロセスにより、agg出力ファイルが完全に自動的に、新しい最新値で定期的に更新されるようになります。以下は、ドキュメントページから直接取得したスクリプトファイルの例です。
# gs_sh script file (sample.gsh)
# If no table exists, create a partitioning table with intervals of 30 days to output data.
CREATE TABLE IF NOT EXISTS etl_output (ts TIMESTAMP PRIMARY KEY, value DOUBLE)
PARTITION BY RANGE (ts) EVERY (30, DAY);
# Retrieve the last run time registered. If it does not exist, retrieve the time one hour before the present.
SELECT case when MAX(ts) ISNULL THEN TIMESTAMP_ADD(HOUR,NOW(),-1) else MAX(ts)
end AS lasttime FROM etl_output;
# Store the retrieved time in a variable.
getval LastTime
# Set the aggregation range between the time retrieved and the present time and obtain the average value for every 20 seconds. Register or update the results into the output container.
INSERT OR REPLACE INTO etl_output (ts, value)
SELECT ts,avg(value) FROM etl_input
WHERE ts BETWEEN TIMESTAMP('$LastTime') AND NOW()
GROUP BY RANGE(ts) EVERY (20, SECOND);
この例では、etl_inputからの集約結果をetl_outputに配置しています。なかなかいい感じですね!
If you have any questions about the blog, please create a Stack Overflow post here https://stackoverflow.com/questions/ask?tags=griddb .
Make sure that you use the “griddb” tag so our engineers can quickly reply to your questions.
