インテージテクノスフィア技術ブログ

株式会社インテージテクノスフィアの社員達がシステム開発や仕事に関する技術情報を随時掲載いたします

influxDBを触ってみた

こんにちはアイダです。今回は influxdb を業務適用して大変面白かった!というSさんが投稿してくれました。アイダは influxdb を全く知らなかったのですが、いわゆるIoT的な開発にもDBがあるのですね。タイトルに偽りありの使いっぷりですよ!

発端

とある製造業のお客様とIoT関連の分析業務をおこなっていて、下記のような課題に直面した。

  • データ発生元や発生タイミング(つまり粒度)、種類(数値と文字)の異なるデータの特定の時間軸を串刺しで参照したい
  • Web画面等からから大量の蓄積されている時系列データを任意の時間間隔でサンプリングしたい

さーて、どうするか。 サンプリングの間隔や集計は抽出ロジックでなんとなくどうにでもなる気がしたが、問題なのは特定の時間軸を基準としてデータを引っ張ろうとした場合に実データが発生していない粒度の異なるデータをどうやってひっぱってくるか??それも数値以外の属性も含めて。 いろいろ調べていくうちに、influxDBという時系列DBの集計関数にfillオプションというモノがあるようだ…というわけで今回触ってみた。

時系列DBとは、influxdbとは

そもそも、

  • 時系列DBってなんなの?
  • 時系列DBってどういう種類があるの?
  • influxdbでは何ができるの?他にどんな機能があるの?

とお思いかとおもいますが、そこは世の中にスバラシイ記事がたくさんあるのでそちらにお任せしたいと思います。
もしどーしてもそちらが気になる方は、まずは下記サイトがとても参考になるのでご参照ください。

influxDB 開発元 influxdata サイト

https://www.influxdata.com/

今、時系列DBが熱い(熱いとは言っていない?)

https://www.slideshare.net/nmrmsys/db-94629867

時系列データベースの比較

https://qiita.com/kanegoon/items/054b53c4c15609d32d3a

サンプルデータの準備

それでは下記の例をもとにまずはinfluxDBにデータを準備していきます。 データに動きがないと面白くないので、例えば車を動かした場合のイメージでデータを考えてみました。

2020/4/1 15:00よりインテージテクノスフィア本社敷地内からひばりヶ丘駅ロータリへ車を走らせたが、途中の信号機待ちで諸事情によりドライバー変更したとする(あくまで仮です!)。 その時の位置情報と移動速度を別々のスマホアプリから取得し、さらに運転手の変更記録もあとから登録した。

データ種類 データ頻度
A GPS緯度 30秒ごとにデータ発生
B GPS経度 30秒ごとにデータ発生
C スピード 10秒ごとにデータ発生
D 運転手 15:01:10にAさんからBさんへ交代

データイメージ

time lat lon spd driver
2020-04-01 15:00:00 35.749103 139.542589 0 Aさん
2020-04-01 15:00:10     10  
2020-04-01 15:00:20     30  
2020-04-01 15:00:30 35.748833 139.543168 40  
2020-04-01 15:00:40     42  
2020-04-01 15:00:50     25  
2020-04-01 15:01:00 35.749486 139.543512 0  
2020-04-01 15:01:10     10 Bさん
2020-04-01 15:01:20     20  
2020-04-01 15:01:30 35.750322 139.544359 30  
2020-04-01 15:01:40     40  
2020-04-01 15:01:50     40  
2020-04-01 15:02:00 35.751141 139.545014 40  
2020-04-01 15:02:10     40  
2020-04-01 15:02:20     40  
2020-04-01 15:02:30 35.751768 139.545582 22  
2020-04-01 15:02:40     5  
2020-04-01 15:02:50     0  

やりたいこと

  1. 歯抜け部分を埋めたい、埋め方は直近のデータを引き継いで持ってきてよい
  2. 15:02:40時点のデータを串刺しで持ってきたい、もしデータがない場合にはその中で一番新しいデータを引き継いで持ってきてよい
  3. 今回の走行における代表データをほしい、位置やドライバーは最初のデータを代表値としセンサーのデータは走行時間の平均値を使う

データの作成

環境構築やRDBMSのテーブルに該当するmeasurement等、時系列DB特有の機能の説明はここでは省きます。

データ登録SQL(正確にはinfluxQL)

insert test lat=35.749103 1585720800000000000
insert test lat=35.748833 1585720830000000000
insert test lat=35.749486 1585720860000000000
insert test lat=35.750322 1585720890000000000
insert test lat=35.751141 1585720920000000000
insert test lat=35.751768 1585720950000000000
insert test lon=139.542589 1585720800000000000
insert test lon=139.543168 1585720830000000000
insert test lon=139.543512 1585720860000000000
insert test lon=139.544359 1585720890000000000
insert test lon=139.545014 1585720920000000000
insert test lon=139.545582 1585720950000000000
insert test spd=0 1585720800000000000
insert test spd=10 1585720810000000000
insert test spd=30 1585720820000000000
insert test spd=40 1585720830000000000
insert test spd=42 1585720840000000000
insert test spd=25 1585720850000000000
insert test spd=0 1585720860000000000
insert test spd=10 1585720870000000000
insert test spd=20 1585720880000000000
insert test spd=30 1585720890000000000
insert test spd=40 1585720900000000000
insert test spd=40 1585720910000000000
insert test spd=40 1585720920000000000
insert test spd=40 1585720930000000000
insert test spd=40 1585720940000000000
insert test spd=22 1585720950000000000
insert test spd=5 1585720960000000000
insert test spd=0 1585720970000000000
insert test driver="Aさん" 1585720800000000000
insert test driver="Bさん" 1585720870000000000

insert文の後ろについてる数字はなにかって? そう、UNIX時間です。。

確認SQL

> select * from test
name: test
time                 driver lat       lon        spd
----                 ------ ---       ---        ---
2020-04-01T06:00:00Z Aさん    35.749103 139.542589 0
2020-04-01T06:00:10Z                             10
2020-04-01T06:00:20Z                             30
2020-04-01T06:00:30Z        35.748833 139.543168 40
2020-04-01T06:00:40Z                             42
2020-04-01T06:00:50Z                             25
2020-04-01T06:01:00Z        35.749486 139.543512 0
2020-04-01T06:01:10Z Bさん                         10
2020-04-01T06:01:20Z                             20
2020-04-01T06:01:30Z        35.750322 139.544359 30
2020-04-01T06:01:40Z                             40
2020-04-01T06:01:50Z                             40
2020-04-01T06:02:00Z        35.751141 139.545014 40
2020-04-01T06:02:10Z                             40
2020-04-01T06:02:20Z                             40
2020-04-01T06:02:30Z        35.751768 139.545582 22
2020-04-01T06:02:40Z                             5
2020-04-01T06:02:50Z                             0
>

よし、イメージどおりのデータができた!!

触ってみた

じゃあ、やりたいことをどうやってできるか、やってみよう。
つべこべ言わない、見てくれ。

1. 歯抜け部分を埋めたい、埋め方は直近のデータを引き継いで持ってきてよい

> select 
     last(driver) as driver, 
     last(lat) as lat,
     last(lon) as lon,
     last(spd) as spd 
   from test 
   where 
     time >= '2020-04-01T06:00:00Z' and 
     time <= '2020-04-01T06:03:00Z' 
   group by 
     time(10s) fill(previous)

name: test
time                 driver lat       lon        spd
----                 ------ ---       ---        ---
2020-04-01T06:00:00Z Aさん    35.749103 139.542589 0
2020-04-01T06:00:10Z Aさん    35.749103 139.542589 10
2020-04-01T06:00:20Z Aさん    35.749103 139.542589 30
2020-04-01T06:00:30Z Aさん    35.748833 139.543168 40
2020-04-01T06:00:40Z Aさん    35.748833 139.543168 42
2020-04-01T06:00:50Z Aさん    35.748833 139.543168 25
2020-04-01T06:01:00Z Aさん    35.749486 139.543512 0
2020-04-01T06:01:10Z Bさん    35.749486 139.543512 10
2020-04-01T06:01:20Z Bさん    35.749486 139.543512 20
2020-04-01T06:01:30Z Bさん    35.750322 139.544359 30
2020-04-01T06:01:40Z Bさん    35.750322 139.544359 40
2020-04-01T06:01:50Z Bさん    35.750322 139.544359 40
2020-04-01T06:02:00Z Bさん    35.751141 139.545014 40
2020-04-01T06:02:10Z Bさん    35.751141 139.545014 40
2020-04-01T06:02:20Z Bさん    35.751141 139.545014 40
2020-04-01T06:02:30Z Bさん    35.751768 139.545582 22
2020-04-01T06:02:40Z Bさん    35.751768 139.545582 5
2020-04-01T06:02:50Z Bさん    35.751768 139.545582 0
2020-04-01T06:03:00Z Bさん    35.751768 139.545582 0

うっひょー!!!!
うれしすぎない?!文字情報も含めて1発でできちゃったよ。
ポイントは集計関数のlastと、グループ化関数についているfillオプションです。
この指定で歯抜けを穴埋めしてくれる、それも文字情報も含めて!
ちなみに他の時系列DBも少し調べてみたのですが文字情報にも適用できるこのようなグループ集計機能をもっていそうなのはinfluxdbだけでした 。
(いやいやこっちにも似たようなのあるよ、といった場合には申し訳ございません。別にinfluxの回し者ではありません。)

2. 15:02:40時点のデータを串刺しで持ってきたい、もしデータがない場合にはその中で一番新しいデータを引き継いで持ってきてよい

> select
     last(driver) as driver,
     last(lat) as lat,
     last(lon) as lon,
     last(spd) as spd 
   from test 
   where 
     time >= '2020-04-01T06:00:00Z' and 
     time <= '2020-04-01T06:02:40Z' 
   group by 
     time(10s) fill(previous) 
   offset 16

name: test
time                 driver lat       lon        spd
----                 ------ ---       ---        ---
2020-04-01T06:02:40Z Bさん    35.751768 139.545582 5

1ができれば簡単だよね・・・とおもったら、今回考えついたやり方はこのようになりました。
having句つかえないの?とかサブクエリによるorder byとlimitでやれよとかいろいろご指摘点はごもっともなんですが、とりあえずうまく動いたのはこの方法(where句による範囲指定とoffsetによる開始位置の指定)になりました。
このinfluxQL癖が強いの~

3. 今回の走行における1時間範囲の代表データをほしい、位置やドライバーは最初のデータを代表値としセンサーのデータは走行時間の平均値を使う

> select 
     first(driver) as driver,
     first(lat) as lat,
     first(lon) as lon,
     mean(spd) as spd 
   from test 
   where 
     time >= '2020-04-01T06:00:00Z' and 
     time <= '2020-04-01T06:03:00Z' 
   group by 
     time(1h) fill(previous)

name: test
time                 driver lat       lon        spd
----                 ------ ---       ---        ---
2020-04-01T06:00:00Z Aさん    35.749103 139.542589 24.11111111111111
> 

これも便利だ!!
グループ集計の時間軸timeを1hとしている事がポイントです。これも文字情報に使えるというのがうれし。
これができればデータ連携処理時にサンプリングロジックとか考えなくてもいし、当然レスポンス速度のために全データを予め画面側で持つ必要もない。 見たい時間軸で集計したサンプリング結果だけを連携すればいいだけ。

まとめ

このような加工操作はpython使われる方であればpandasとかでやる人が多いと思いますが、時間間隔でのサンプリングはめんどくさかったり、そもそもビッグデータ向けに処理化しようとするとメモリ食いつぶし問題とか処理性能問題にぶち当たります。(Sparkとか使える環境をもっていれば別ですが…)
それを解決できる可能性があるのが時系列DBとなります。もし時系列が前提のデータで異なる粒度、種類の大量データをサンプリングする必要があるケースがあった場合はinfluxDBの導入検討をおススメします。

このほか、timestampをindexにしたparquetフォーマットファイルはそのままインポートできたりとか、いろいろ便利機能があります。 どうぞ、お試しあれ!!