Yakstは、海外の役立つブログ記事などを人力で翻訳して公開するプロジェクトです。
9日前投稿

InfluxDBの内部構造入門 パート1

InfluxDBの内部構造についてのまとめをご紹介します。3部構成のうちこのエントリではデータをどのように永続化し、圧縮するかについてを取り扱います。

原文
InfluxDB Internals 101 - part one | InfluxData (English)
原文著者
RYAN BETTS
翻訳依頼者
B5aa4f809000b9147289650532e83932 D98ee74ffe0fafbdc83b23907dda3665
翻訳者
B5aa4f809000b9147289650532e83932 taka-h
翻訳レビュアー
D98ee74ffe0fafbdc83b23907dda3665 doublemarket
原著者への翻訳報告
10日前 Twitterで報告済み 10日前 原著者承諾済み 編集


InfluxDBの内部構造についての入門セッションをInfluxDBの新しいメンバー向けにPaul Dixが開催してくれました。 多くのことを学びましたのでコミュニティーに内容を共有したいと思います。私のInfluxDBへの理解を整理するためにも、同時にもしかするとInfluxDBがどのようなアーキテクチャーかを学びたいと思っている人の役にたつように、この記事を書いています。この連載のゴールはInfluxDBのアーキテクチャに関する概要を整理された状態でご紹介することです。

内容がたくさんありますので、3つのパートに分けてお伝えします。最初の投稿ではデータモデルと書込みについて、2つ目の投稿ではクエリーについて、3つ目の投稿ではInfluxDB Enterpriseのクラスタリングについて説明します。

目次

  • 1. データモデルと書込みについて: InfluxDBへのデータの追加
    • データモデルと用語
    • クライアントからリクエスト(points)を受け付ける
    • ストレージに永続化する
    • 永続化されたデータを圧縮する
  • 2. クエリーについて: InfluxDBからデータを読込む
    • インデックスを作成する
    • TSI(time series index: ディスク上のインデックス)について
    • パースと実行計画
    • クエリーの実行
    • IFQL(※)について
    • DELETEとDROPについて: InfluxDBからのデータを削除
    • データの更新
  • 3. クラスタリング: InfluxDB Enterprise
    • メタデータサービスを理解する
    • データノードを理解する
    • データの分散およびレプリケーションについて

※訳注: SQLライクなInfluxQLとは異なる拡張されたクエリー言語。下記参照

  1. https://www.influxdata.com/blog/influxdb-now-supports-prometheus-remote-read-write-natively/
  2. https://www.influxdata.com/blog/new-data-model-query-language-influxdb-kapacitor/

データモデルと書込みについて: InfluxDBへのデータの追加

デーモデルと用語

InfluxDBのデータベースはポイント(points)を保存します。ポイントは4つの要素から構成されます。メジャメント(measurement)、タグセット(tagset)、フィールドセット(fieldset)、そしてタイムスタンプ(timestamp)です。

メジャメント(measurement)は、タグセットあるいはフィールドセットが異なる関連するポイントを関連付けるために利用することができます。タグセットはポイントともに保存するキーバリューのディクショナリです。フィールドセットは型付きのスカラー値の集合 、すなわちポイントで保存されるデータです。

ポイントのシリアライゼーション形式は、「ラインプロトコル」にて定義されます。 仕様に記載しているポイントの例をみると用語が説明しやすいのでみてみましょう。

temperature,machine=unit42,type=assembly internal=32,external=100 1434055562000000035

メジャメントtemperature(温度)です。

タグセットは、machine=unit42,type=assemblyです。キーはmachinetypeで、これはタグセット内ではタグキー(tag keys)と呼ばれます。値はunit42assemblyタグセット内でこれはタグ値(tag values)とと呼ばれます。

フィールドセットinternal=32,external=100です。キーはinternalexternalで、これはフィールドセットではフィールドキー(field keys)と呼ばれます。値は32および100フィールドセットではこれはフィールド値(field values)と呼ばれます。

それぞれのポイントはただ1つのデータベース(database)にただ1つのリテンションポリシー(retention policy)で保存されます。データベースは複数のユーザーに対する、複数のリテンションポリシー、そして複数のポイントの入れ物です。リテンションポリシーはInfluxDBがどれだけポイントのデータをを保持するか(保存期間)、クラスターでいくつ複製を持つか(レプリケーションファクター)、そしてシャードグループでカバーされるタイムレンジ(シャードグループ保存期間)を設定します。リテンションポリシーを利用することによって、ユーザーが(また、データベースが効率的に)不要となったデータを簡単に破棄できるようになります。これは時系列のアプリケーションでは共通することです。

レプリケーションファクター(replication factor)、シャードグループ(shard groups)、そしてシャード(shard)については後ほどInfluxDBの書込みの動作をご紹介する際に説明します。

もう1つ最初に抑えるべき用語があります。シリーズ(series)です。シリーズはリテンションポリシーメジャメントタグセットを合わせたものを単純に短くしたものです。全く同じリテンションポリシーメジャメント、そしてタグセットを持つ全てのポイントは同じシリーズのメンバーです。

これらの用語、あるいはこの一連のブログ投稿で利用される他の用語については用語集のドキュメントもご活用ください。

クライアントからリクエスト(points)を受け付ける

クライアントはポイントを(ラインプロトコル形式で)InfluxDBのHTTP /writeエンドポイントにPOSTします。ポイントは個別に送ることも可能ですが、効率化するために殆どのアプリケーションはバッチでポイントを送ります。典型的なバッチではポイントは数百から数千のサイズとなります。POSTでは、クエリーパラメーターでデータベースとオプションで付与するリテンションポリシーを指定します。リテンションポリシーが指定されない場合は、デフォルトのリテンションポリシーが利用されます。リクエストボディーに含まれる全てのポイントはそのデータベースおよびリテンションポリシーで書込まれます。POSTのボディーに記述するポイントは、任意の数のシリーズで構成可能です。すなわち、バッチ内のポイントは同一のメジャメントあるいはタグセットである必要はありません。

データベースが新しいポイントを受け取ったら次の2点が必要です。1つ目はデータベースまたはサーバーがクラッシュした際に復元できるようこれらのポイントを永続化すること、2つ目はポイントを問合せ可能(queryable)にすることです。この投稿ではポイントを永続化する前半部分について焦点をあてています。

ストレージに永続化する

ポイントを永続化するためには、それぞれのバッチは書きこまれ先行書き込みログ(WAL)にfsyncされます。WALはデータベースをリカバリーする際のみ読込まれる追記のみを行うファイルです。ディスク容量とディスクIOを効率化するために、WAL内の各バッチはディスクに書込まれる前にsnappyアルゴリズムで圧縮されます。

WALは入力データを永続化するために形式を効率化しますが、これは読込に対してとても良くない形式です。すなわち、クエリーをサポートするのには適さない形式となります。新しいデータに対してすぐにクエリーが発行できるようにするために、入力のポイントはメモリー上のキャッシュ(cache)にも書込まれます。キャッシュは参照および挿入の性能が最適化されるようなメモリー内のデータ構造をとります。これは時間でソートしたフィールドのリストに対するシリーズのマップです。

WALは新しいポイントを永続化し、キャッシュは新しいポイントに対してクエリー可能とします。キャッシュTSMファイルに書き込まれる前にシステムがクラッシュまたはシャットダウンされた場合には、データベースが起動した際にWALに保存されたバッチを読み込んでリプレーすることで再構成されます。

WALキャッシュの組み合わせは入力データに対してよく機能しますが、長期間の保存には不十分です。WALは起動時に必ずリプレーされますので、これは妥当なサイズに制限することが重要です。キャッシュはRAMのサイズに制限され、これは多くの時系列データの利用例から考えると理想的ではありません。すなわち、データは(データベースに多くのポイントを保存できるよう)容量を効率化し効率的にクエリーを実行できるようにデータを再構成し、ディスク上の長期間に渡る保存領域に書込む必要があります。

時系列のクエリーはある期間に渡る集約、例えばある限られた時刻範囲のポイントを読込み平均、最大、あるいはウィンドウ関数のような集約関数で分析する、といったものであることが多いです。データを行ではなく列ベースに再構成しディスクに保存する、列指向データベースのデータ保存の技術は、このクエリーパターンにはとても適しています。その上、列形式のシステムはデータの圧縮が格段に優れていますので、データを効率的に保存する需要を満たします。列の保存については多くの文献があり、[列指向データベースシステム]はこの概要を記載したドキュメントの1つです。

時系列のアプリケーションではある一定期間の後にデータを強制的に削除することがしばしばあります。多くの監視アプリケーションでは、例えば、監視のクエリーに対応するために直近の1ヶ月あるいは2ヶ月のデータを保存します。設定されたデータ保持期間を超えた場合にデータベースからデータを効率的に削除できる必要があります。列指向ストレージからポイントを削除するのは高コストですので、InfluxDBでは列指向の形式を時間で区切ったチャンクとして再構成しています。保存期間が過ぎたら永続化されたデータに対して大規模な更新処理をする必要はなく、ファイルシステム上から時間で区切られたファイルがただ単に削除されます。

最後にInfluxDBがクラスター構成で運用されている際には、障害発生時に可用性および堅牢性が保たれるようにデータを複数サーバーで複製します。

オプションとして指定できるデータ保存期間、保存期間内の時間ブロックの粒度、データ複製数はInfluxDBのリテンションポリシーで設定します。

CREATE RETENTION POLICY {retention_policy_name} ON {database_name} DURATION {duration} REPLICATION {n} [SHARD DURATION {duration}] [DEFAULT]

※訳注: 表示上の問題から原文の<, >{, }に変えて表示

durationはオプションで指定するデータ保持期間です(データを破棄しないのであれば、durationINFに設定してください)。SHARD DURATIONはデータ保持期間内のデータの粒度です。例えば、24時間のdurationshard durationを1時間に設定すると、データベースは24の1時間のシャードで保存するよう設定します。毎時間一番古いシャードがデータベースから削除されます。レプリケーションファクター、つまりクラスター内でシャードの複製をいくつ保持するか、を設定するにはREPLICATIONを指定してください。

具体的には、データベースは次の物理配置でディスク上にデータを作成します。

'' Database director  /db
    '' Retention Policy directory /db/rp
        '' Shard Group (time bounded). (Logical)
            '' Shard directory (db/rp/Id#)
                '' TSM0001.tsm (data file)
                '' TSM0002.tsm (data file)
                '' …

メモリー内のキャッシュはディスクにTSM形式でフラッシュされます。フラッシュが完了すると、フラッシュしたポイントはキャッシュおよび対応するWALから削除されます(WALとキャッシュはシャードごとに管理されます)。TSMのデータファイルはポイントを列指向に構成した上で保存しています。一度書き込まれればTSMファイルは以後変更されません。TSMのファイル形式についての詳細についてはInfluxDBのドキュメントをご確認ください。

TSMデータを圧縮する

キャッシュは比較的少量のデータです。TSMの列指向の形式はある1つのブロックのシリーズに長期間のデータを保存するとき一番うまく動作します。期間が長くなれば圧縮効率がよくなり、クエリーに対するフィールドをスキャンする時間を低減できます。TSMの形式は基本的にlog-structured merge-treeアルゴリズムに大きく依存しています。新しい(レベル1の)TSMファイルはキャッシュがフラッシュされた際に生成されます。これらのファイルは後ほどレベル2のファイルに結合(圧縮)されます。レベル2のファイルはさらに、レベル3のファイルに結合されます。高位の圧縮はファイルが大きくなり結果としてコールドに(そのタイムレンジが書込みに対してホットでなくなった場合)なった際に発生します。上記で参照しているドキュメントで、圧縮についての詳細がご覧になれます。

TSMの圧縮のソースコード内では多くのロジックがあり高度化されています。しかし、高位のゴールはとてもシンプルです。すなわち、あるシリーズに対して圧縮効率およびクエリーに対する読込性能が最善となるよう長期間に渡りデータを統合し最適化する、ということです。

パート1のまとめ

まとめとしては、ポイントがバッチでInfluxDBにPOSTされます。このバッチはすぐに永続化するためにsnappyアルゴリズムで圧縮されWALとして書込まれます。ポイントは新しく書き込まれたポイントに対してすぐにクエリーが発行可能となるよう、メモリー内のキャッシュにも書き込まれます。キャッシュは定期的にTSMファイルにフラッシュされます。TSMファイルが蓄積されると、これらは結合そして圧縮され高位のTSMファイルとなります。TSMのデータはシャードに分かれて保存されており、シャードでカバーされる時間レンジやクラスター構成の際のレプリケーションファクターがリテンションポリシーとして設定されます。

この投稿がInfluxDBが書き込みに対して、どのようにデータを受け取り永続化しているかについての説明としてお役に立てればと思います。次の投稿では、参照、更新、削除の操作をどのように実現しているかを取り扱う予定です。

次の記事
InfluxDBのOpenTracing対応: 分散トレーシングのオープンスタンダート
前の記事
MySQLの高可用性構成の展望2017年版(赤ちゃん編)

Feed small 記事フィード