InfluxDB をちょっとさわってみた

InfluxDBとは

メトリクスやイベントといった時系列データを格納するのに適したデータストアです。

ちなみに go で書かれています。

ちなみに 2013のOpen Source Rookiesに選ばれました。

InfluxDBの特徴

RRDやMySQLに時系列データを格納する場合と比較して、InfluxDBの特徴を紹介します。

バックエンドは LevelDB

LevelDBとは、キーでソートされた状態で可能されたKVSです(Google製)。詳しくはこのへん参照のこと。

将来的にLevelDBからほかの実装に変わる可能性もありそうです。

HTTP API

データの登録や問い合わせ等、HTTP APIで行います。問い合わせ結果は JSON で返ってきます。

ユーザー認証はquery stringかBasic認証で行えます。

databaseとseries

RDBMSのdatabaseとtableと同じく、databaseとseriesの2階層でデータの管理ができます。

schema-less

RDBMSのテーブル定義やRRDのdata sourceの定義と違い、格納するデータ構造を予め定義しておく必要がありません。

過去のデータも登録可能

RRDと違って、任意の時点のデータを登録可能です。

SQL風の問い合わせ言語

結果はPerlの構造体で書いてますが、ほんとうはJSONで返ってきています。

  • 時刻で範囲指定
select reqtime, url from web9999.httpd where time > now() - 1h limit 1000;

[
  {
    columns => ["time","sequence_number","reqtime","url"],
    name => "web9999.httpd",
    points => [
                ["1392030886","60703580001","0.21","/view/entry"],
                ["1392030885","60703560001","0.07","/login/error"],
                ["1392030884","60703540001","0.32","/login/"],
                ...
              ]
  }
]
  • カラムの値で絞り込み
select reqtime, url from web9999.httpd where reqtime > 2.5;

[
  {
    columns => ["time","sequence_number","reqtime","url"],
    name => "web9999.httpd",
    points => [
                ["1392030883","60703520001","2.81","/list/entry"],
                ["1392030878","60703420001","2.72","/view/entry"],
                ["1392030877","60703400001","2.94","/login/error"],
                ...
              ]
  }
]
select reqtime, url from web9999.httpd where url =~ /^\/login\//;

[
  {
    columns => ["time","sequence_number","reqtime","url"],
    name => "web9999.httpd",
    points => [
                ["1392030885","60703560001","0.07","/login/error"],
                ["1392030884","60703540001","0.32","/login/"],
                ["1392030881","60703480001","0.78","/login/error"],
                ...
              ]
  }
]
  • 複数seriesに同時に問い合わせ
select * from /web9999\..*/;

[
  {
    columns => ["time","sequence_number","sys","user"],
    name => "web9999.cpu",
    points => [
                ["1392030886","60703590001",3,57],
                ["1392030885","60703570001",9,1],
                ["1392030884","60703550001",28,42],
                ...
              ]
  }
  {
    columns => ["time","sequence_number","reqtime","url"],
    name => "web9999.httpd",
    points => [
                ["1392030886","60703580001","0.21","/view/entry"],
                ["1392030885","60703560001","0.07","/login/error"],
                ["1392030884","60703540001","0.32","/login/"],
                ...
              ]
  }
]
  • 複数seriesに問い合わせ、結果をmergeする
select reqtime, url from web9999.httpd merge web0001.httpd;

[
  {
    columns => ["time","sequence_number","reqtime","url","_orig_series"],
    name => "web9999.httpd_merge_web0001.httpd",
    points => [
                ["1392030886","60706000001","1.09","/view/entry","web0001.httpd"],
                ["1392030886","60703580001","0.21","/view/entry","web9999.httpd"],
                ["1392030885","60705980001","1.53","/login/error","web0001.httpd"],
                ...
              ]
  }
]

select reqtime, url from merge /.*\.httpd$/
のように、merge対象のseriesを正規表現で指定できるとドキュメントには書いてありますが、まだ実装されてないみたいです。 https://github.com/influxdb/influxdb/issues/72

集約関数が豊富

COUNTやMIN, MAX, MEANなどなど。

RRDのDERIVEと同等のことができるDERIVATIVEや、パーセンタイルを算出するPERCENTILEもあります。

MIN,MAXはRDBMSで算出するのはめんどうくさい、DERIVATIVEはかなりめんどくさいので便利だと思います。

ちなみにDERIVATIVEとは、( v(t1) - v(t0) ) / (t1 - t0) を時刻 t1 の値とする関数です。具体的には、ネットワークの転送バイト数など、増加カウンタの値をデータとして格納しておき、グラフ描写時には Mbps (増加割合)を計算して使いたいときに便利です。

GROUP BY によるダウンサンプリングが可能

例えば1分ごとのデータを格納している場合、

select mean(sys) from web9999.cpu group by time(15m);

で、15分おきにまびいてその間の平均値を求められます。

RRDでは一つのDSについて、保持粒度の異なる複数のRRAを定義しますが、それと同じ事をオンデマンドでできるわけです。

Continuous Query

問い合わせの度にいちいちGROUP BYでダウンサンプリングを行うのは効率的ではありません。そこで Continuous Query というものを登録しておくことで、定期的にダウンサンプリングを行いデータをまびくことができます。

用途を限定したRDBMSのtriggerのようなものです。


例えば

select mean(sys) from web9999.cpu group by time(15m)
  into web9999.cpu.15m;

のような Continuous Query を登録しておくと、自動的に web9999.cpu.15m にまびいたデータを入れてくれます。

この Continuous Query と次の Regularly Scheduled Deletes (後述)を活用すると、

  • もっとも粒度の小さい1分おきのデータは2週間分保持
  • 15分の平均データは2ヶ月分保持
  • 60分の平均データは永久保持

といったことが可能になりそうです。

Regularly Scheduled Deletes

指定した期間より古いデータを自動的に削除する機能があるようですが、InfluxDB 0.5.4ではまだ実装されていないようです。 https://github.com/influxdb/influxdb/issues/98

InfluxDB のいまいちなカワイイところ

InfluxDB v0.5.4 (git: 93e93fa8986ab782b60087aa5b194113af8e16e5) での感想です。

将来的には修正されるものもあると思うので注意してください。

データとして構造をもったJSONを登録できない

そういう仕様なので、フラットに展開して入れるしかないと思います。

{
  "a": {
    "i": "foo",
    "j": "bar",
  },
  "b": 3
}

{
  "a.i": "foo",
  "a.j": "bar",
  "b": 3
}

のような感じに平坦化するとか。

集約関数を複数カラムに使えない (修正済み)

select max(col1), max(col1) の結果の columns は ["max", "max"] になるので、データは返ってくるけどどっちが col1 の max が区別ができない。

SQLの様にカラムにもasで別名が付けられるようになればいいんだけど。(seriesにはasで別名をつけられる)

issue にも上がっているのでそのうち対応されるかも。

https://github.com/influxdb/influxdb/issues/69 → 対応されました! v0.5.0-rc3にはカラムの別名が使えるようになると思います。

v0.5.1だとasを使ってこんな感じでできるようになりました。

[9] pry(main)> select mean(sys) as sys, mean(user) as user from myhost.cpu group by time(10m) limit 10;
+---------------+--------------------+--------------------+
|                       myhost.cpu                        |
+---------------+--------------------+--------------------+
| time          | sys                | user               |
+---------------+--------------------+--------------------+
| 1359676200000 | 23                 | 52.599999999999994 |
| 1359675600000 | 28.9               | 41.29999999999999  |
カラム名予約語をつけると問い合わせ不能になる

例えば「in」というカラム名をつけると、select in from がエラーになる。select 'in' from とすればいいんだけど、今度は集約関数が使えなくなる。select count('in') は動かない。

あるdatabase内のseriesの一覧を得る API がない

select * from /./ limit 1で代用するしかないみたい。

と思っていたがlist series というクエリーを投げればよい。

が!、バグってて動かない。https://github.com/influxdb/influxdb/issues/358

データのバックアップ方法が不明

公式ドキュメントにバックアップについて書いてある文章がない。

LevelDBのデータファイルをファイルコピーすればいいのかもしれないけど、整合性が保たれるか、バックアップデータとして正常に使えるのか不明。

バックアップに限らず運用周りはこれからの感じがする。

目玉の Continuous Query 周りがいろいろアレ

database を削除しても Continuous Query の定義は消えないで残る。(仕様?)

前述の通り、カラム名の別名付与ができないので、select max(sys), max(user) from myhost.cpu into ... な Continuous Query を定義してselectすると、maxが2つ返ってきて区別できない。

今のところ、カラムごとに別の Continuous Query を定義するしかない。→カラムの別名が実装されたv0.5.0-rc3以降なら問題ないかも

v0.5.1だとこんな感じでできるようになりました!!

[25] pry(main)> select mean(sys) as sys, mean(user) as user from myhost.cpu group by time(10m) into myhost.cpu.10m
 
[24] pry(main)> select * from myhost.cpu.10m limit 10;
+---------------+-----------------+--------------------+--------------------+
|                              myhost.cpu.10m                               |
+---------------+-----------------+--------------------+--------------------+
| time          | sequence_number | sys                | user               |
+---------------+-----------------+--------------------+--------------------+
| 1359676200000 | 1               | 23                 | 52.599999999999994 |
| 1359675600000 | 1               | 28.9               | 41.29999999999999  |

Continuous Queryを定義した時点で、当該seriesにある前データ(過去のも)を処理してダウンサンプリングする。過去のデータを対象にできるのはこのときのみ。

なので、CQを定義した後で過去のデータを入れてもダウンサンプリングされない。検証時に過去のデータを入れてる時や、実運用でも過去のデータを入れる要件がある場合は注意。

ただ、再計算できるようにするというissueが上がっているので要注目。
https://github.com/influxdb/influxdb/issues/211

ほか

rootのパスワードの変更

↓で、できます。

curl -u root:root -X POST 'http://127.0.0.1:8086/cluster_admins/root' -d '{"password": "root"}'
PerlからInfluxDBにアクセスするのは

拙作の InfluxDB を使うといいと思います!