オープンソース時系列データベースの分析 - Part1

この記事では、人気のある**オープンソースの時系列データベースエンジン**の時系列データの保存と計算能力を分析しています。

周 肇峰著

#人気のあるオープンソース時系列データベース

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/130fb1da-9213-0aac-c108-22b3fd7683d7.png)


下図は、2019年4月時点でのDB-Engines上での時系列データベースのランキングです。詳細な分析のために、オープンソースと分散型の時系列データベースを選びました。ランキング上位10位には、スタンドアロンのストレージエンジンとして定評のあるRRDがランクインしています。Graphiteの最下層はWhisperで、最適化されたより強力なRRDデータベースと考えてよいでしょう。Kdb+とeXtremeDBはオープンソースではないため、分析の対象には含まれていません。オープンソースのInfluxDBとPrometheusの最下層は、いずれもlevelDBをベースに開発されたスタンドアロンのストレージエンジンです。商用のInfluxDBは分散型ストレージエンジンをサポートしており、Prometheusのロードマップでも分散型ストレージエンジンをサポートする予定です。

最終的には、OpenTSDB、KairosDB、InfluxDBを選択して詳細な分析を行いました。私はOpenTSDBに精通しており、そのソースコードを研究してきたので、OpenTSDBについては余計に詳しく記述しています。ただし、他の時系列データベースについてはあまり知らないので、分析の際に間違っていたら訂正しても構いません。


#OpenTSDB
OpenTSDBは分散型でスケーラブルな時系列データベースで、1秒間に数百万エントリまでの書き込み速度をサポートし、ミリ秒レベルの精度でのデータ保存をサポートし、精度を犠牲にすることなくデータを永続的に保存します。優れた書き込み性能とストレージ機能は、HBaseに依存するボトムレイヤーによるものです。HBaseはLSMツリーストレージエンジンと分散アーキテクチャを使用して優れた書き込み性能を実現していますが、優れたストレージ性能は完全に水平にスケーリングされたHDFSに依存したボトム層によるものです。OpenTSDBはHBaseに深く依存しており、HBaseのボトム層のストレージ構造の属性に応じて、多くの微妙な最適化が行われてきました。最新版では、BigTableやCassandraのサポートもスケーリングされています。

#アーキテクト

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/14753333-6544-fb40-8357-33e05a5cadfb.png)


下図はOpenTSDBのアーキテクチャを示したもので、コアコンポーネントはTSDとHBaseです。TSDはステートレスなノード群であり、任意にスケールすることができ、HBase以外に依存関係を持ちません。TSDはHTTPとTelnetのインターフェースを公開し、データの書き込みと問い合わせをサポートします。HBaseのO&Mがそうでないのに対し、TSDのデプロイとO&Mはステートレス設計のため非常にシンプルです。これは、BigTableやCassandraのサポートをスケールアップする理由の一つでもあります。

#データモデル
OpenTSDBは指標によってモデル化されており、データポイントには以下のコンポーネントが含まれています。

- Metric: sys.cpu.user や stock.quote のような時系列データのインジケータの名前。
- Timestamp: その時点での特定の時間を表す秒またはミリ秒単位のUnixタイムスタンプ。
- タグ: 1つまたは複数のタグ。タグは、対象の異なる次元を記述するために使用されます。タグは、タグ・キーとタグ値で構成されます。タグ・キーはディメンションであり、タグ値はディメンションの値である。
- 値：指標の値。現在のところ、数値型の値のみがサポートされています。


#ストレージモデル
最適化のポイントとなる考え方を簡単にまとめると以下のようになります。

- データを最適化:メトリック、タグキー、タグ値にUniqueIDを割り当て、元の値とUniqueIDのインデックスを確立します。データテーブルには、元の値ではなく、メトリック、タグキー、タグ値に対応するuniqueIDが格納されます。
- キー値の数を最適化:HBaseのボトム層ストレージモデルをよく理解していれば、行の各カラムが格納時にキー値に対応していることがわかると思いますが、そのため、行やカラムの数を減らすことでストレージスペースを大幅に節約でき、クエリの効率を向上させることができます。
- クエリを最適化: HBaseのサーバーサイドフィルタを利用して多次元クエリを最適化し、グループバイや精度削減クエリは事前集約やロールアップを利用して最適化しています。

UIDTable
HBase上のOpenTSDBのいくつかのキーテーブルの構造設計を以下に示します。最初のキーテーブルはtsdb-uidテーブルで、以下のような構造になっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/64c83b3f-31ca-921b-f0ef-daa20db288d6.png)


メトリック、タグキー、タグ値はすべて同じ固定長のuniqueIDが割り当てられ、デフォルトは3バイトです。tsdb-uidテーブルは、2つのカラムファミリを使用して、メトリック、タグキー、タグ値とuniqueIDとのマッピングと逆マッピングを格納しており、合計6つのマップのデータとなっています。

図の例は次のように解釈できます。

- タグキーは'host'であり、対応するuniqueIDは'001'です。
- タグ値は 'static' で、対応する uniqueID は '001' です。
- メトリックは'proc.loadavg.1m'で、対応するユニークIDは'052’です。

各メトリクス、タグキー、タグ値にuniqueIDを割り当てることのメリットは以下の通りです。第一に、記憶領域とデータ転送量が大幅に削減されます。1つの値を3バイトで表現できるので、かなりの圧縮率になります。第二に、固定長のバイトを使用することで、rowkeyから必要な値を容易に解析でき、Javaヒープ内のメモリフットプリントを大幅に削減できる（文字列に比べて、バイトは多くのメモリを節約できる）ため、GCへの負荷が軽減されます。

ただし、固定バイトのUIDエンコーディングでは、UIDの数に上限があります。3 バイトで最大 16777216 の異なる値が許可されており、これはほとんどのシナリオで十分です。長さは調整できますが、動的に変更することはできません。

#データテーブル
第２のキーテーブルは、次のような構造のデータテーブルです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/cafbdff4-1d2e-b33c-6e42-ebdfc00253cf.png)


この表では、同じ時間のデータが同じ行に格納されており、行の各列はデータポイントを表しています。ある行が第2レベルの精度であれば、その行は最大3600ポイントを持つことができ、ミリ秒レベルの精度であれば、その行は最大3600000ポイントを持つことができます。

このテーブルデザインの微妙なところは、rowkeyと修飾子（列名）のデザインと、データの行全体のコンパクションポリシーにあります。rowkeyの書式です。



```
<metric><timestamp><tagk1><tagv1><tagk2>tagv2>...<tagkn><tagvn>
```

メトリック、タグキー、タグ値はすべてUIDで表されます。UIDのバイト長が固定されている属性のため、rowkeyを解析する際には、バイトオフセットを利用して対応する値を簡単に抽出することができます。Qualifierの値は、データポイントのタイムスタンプの時間偏差を時間単位で表したものです。例えば、秒単位の精度のデータであれば、30秒目のデータに対応する時間偏差は30なので、列名の値は30となります。カラム名に時間偏差値を使用するメリットは、記憶容量を大幅に節約できることです。第２レベルのデータには２バイト、ミリ秒レベルのデータには４バイト、完全なタイムスタンプを格納するためには６バイトしか必要ありません。データの行全体が書き込まれた後、OpenTSDBはまた、主にキー値の数を減らすために、行内のすべての列を1つの列にマージするコンパクションポリシーを採用しています。

#クエリの最適化
HBase は、単一行クエリや範囲クエリなどの単純なクエリ操作のみを提供しています。1 行クエリの場合は、完全な rowkey を提供する必要があります。範囲クエリでは、行キーの範囲を提供する必要があり、その範囲内のすべてのデータをスキャンすることで取得することができます。一般的に、1行クエリの速度は非常に速いですが、範囲クエリの速度はスキャン範囲の大きさに依存します。通常は数万行をスキャンしても問題ありませんが、数億行をスキャンすると読み込みの遅延が大きくなります。

OpenTSDBは、豊富なクエリ機能を提供します。それは、任意のタグキーに対するフィルタリング、グループバイや精度削減をサポートしています。タグキーのフィルタリングはクエリの一部であり、グループバイと精度削減はクエリ結果の計算の一部です。クエリ基準では、主なパラメータとして、メトリック名、タグキーのフィルタリング基準、時間範囲を指定します。前節で指摘したように、データテーブルのrowkeyの形式は次のようになります。`<metric><timestamp><tagk1><tagv1><tagk2><tagv2>...<tagkn><tagvn>`となります。クエリのパラメータから、メトリック名と時間範囲が決定されていれば、行キーの少なくとも１つの走査範囲が決定されると結論づけることができます。しかし、この走査範囲は、同じメトリック名と時間範囲を持つタグキーのすべての組み合わせをクエリします。タグキーの組み合わせが多い場合には、走査範囲は制御不能であり、非常に大きくなる可能性があり、したがって、クエリ効率は基本的に受け入れられない。

OpenTSDBのクエリに対する具体的な最適化策は以下の通りです。

- サーバ側のフィルタ

HBaseは、豊富で拡張性の高いフィルタを提供します。フィルタの動作原理は、サーバ側でデータをスキャンしてフィルタリングし、その結果をクライアントに返すというものです。サーバ側フィルタの最適化ポリシーは、スキャンされるデータ量を減らすことはできませんが、送信されるデータ量を大幅に減らすことができます。OpenTSDBでは、特定の条件のタグキーフィルタを、最下層のHBaseのサーバ側フィルタに変換しています。しかし、この最適化の効果は限定的で、クエリに影響を与える最も重要な要因は送信効率ではなく、最下層のレンジスキャンの効率です。

- レンジクエリでスキャンされるデータ量を減らす
クエリの効率を上げるためには、やはりその範囲内でスキャンするデータ量を根本的に減らす必要があります。これは、クエリの範囲を減らすのではなく、その範囲内でスキャンするデータ量を減らすことに注意してください。ここでは、HBaseの重要なフィルタであるFuzzyRowFilterを使用します。FuzzyRowFilterは、指定された条件に従って範囲スキャンを行うと、動的に一定量のデータをスキップすることができます。ただし、この最適化はOpenTSDBが提供するすべてのクエリ条件に適用できるわけではなく、特定の条件を満たす必要がありますので、ここでは詳しくは説明しません。興味のある方はFuzzyRowFilterの原理を知ることができます。



- 範囲クエリを1行クエリに最適化する
今回の最適化は、先ほどの最適化よりも極端になっています。最適化の考え方は非常にわかりやすいです。クエリーするすべてのデータに対応する行キーがわかっていれば、範囲をスキャンする必要はなく、1つの行だけをクエリーすればよいのです。同様に、OpenTSDBが提供しているクエリの基準をすべて満たしていれば最適化を適用できるわけではなく、一定の基準を満たす必要があります。単一行のクエリの場合、特定の行キーが必要であり、データテーブル内の行キーの構成要素には、メトリック名、タイムスタンプ、タグが含まれます。メトリック名とタイムスタンプは決定することができます。タグも決定できれば、完全な行キーを構成することができます。したがって、この最適化を適用するには、すべてのタグキーに対応するタグ値を提供する必要があります。



以上がOpenTSDBのHBaseクエリに対する最適化策の一端ですが、クエリに加えて、クエリされたデータに対してグループバイや精度削減などの処理が必要になります。また、クエリ後の結果の量にもよりますが、groupbyや精度削減の計算オーバーヘッドも非常に大きいものとなっています。groupbyと精度削減の計算の最適化については、ほとんどすべての時系列データベースが同じ最適化手法、すなわち、事前集計と自動ロールアップを採用しています。これは、クエリの後ではなく、事前に計算しようという考え方です。しかし、OpenTSDBは最新バージョンの時点では事前集計やロールアップをサポートしていません。開発中のバージョン2.4では、少しハックなソリューションが提供されています。それは、事前集計やロールアップの結果の書き込みをサポートするための新しいインターフェイスを提供するだけです。ただし、データの事前集計やロールアップの計算は、まだアウターレイヤーでユーザー自身が実装する必要があります。

#まとめ
OpenTSDBの利点は、データの書き込みと保存ができることにありますが、これは主にボトムレイヤーがHBaseに依存していることに起因しています。欠点は、データの問い合わせや分析ができないことです。クエリでは多くの最適化が行われてきたが、これらの最適化はすべてのクエリシナリオに適用できるわけではありません。今回比較するいくつかの時系列データベースの中で、タグ値フィルタリングのクエリ最適化という点ではOpenTSDBが一番悪いという意見もあるかもしれません。事前集計や自動ロールアップのサポートは、グループバイやダウンサンプリングクエリでは利用できません。しかし、機能面ではOpenTSDB APIが最も充実しており、ベンチマークとなっています。



*本ブログは英語版からの翻訳です。オリジナルは[こちら](https://www.alibabacloud.com/blog/a-comprehensive-analysis-of-open-source-time-series-databases-1_594730?spm=a2c65.11461544.0.0.23e753559GvF6A)からご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。*

*アリババクラウドは日本に２つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
[アリババクラウドジャパン公式ページ](https://us.alibabacloud.com/)*