クラウド・プラットフォーム

時系列データベース

センサーデータの保存に適したDB。

概要

時系列データベース(Time Series Database、TSDB)とは、時刻とセットになったデータ(時系列データ)の保存・検索・分析に特化したデータベースシステムです。センサーが一定間隔で記録する温度・圧力・振動などの測定値、株価や為替のティックデータ、サーバーのCPU/メモリ使用率など、「いつ・何の値だったか」を大量かつ高頻度に記録するワークロードに最適化されています。

一般的なRDB(リレーショナルデータベース)でもタイムスタンプ付きのデータは扱えますが、以下の点で時系列DBが圧倒的に有利です。

  • 書き込み性能: 1秒あたり数百万件の時系列データを効率的に追記
  • 圧縮率: 時系列データの特性(連続値、差分が小さい)を活かした高圧縮
  • 時系列クエリ: 時間範囲指定、ダウンサンプリング、移動平均などを簡潔に記述
  • 自動データ保持管理: データ保持期間(Retention Policy)の設定で古いデータを自動削除

IoTシステムでは、数千台のセンサーが毎秒データを送信するため、1日で数十億件のレコードが生成されることも珍しくありません。時系列DBはこの規模のデータを効率的に管理し、任意の時間範囲でのグラフ表示や異常検知クエリを高速に処理します。

歴史・背景

時系列データの保存は古くから必要とされており、1990年代には株式市場向けの「RRDtool」がグラフ生成と組み合わせた時系列データ保存ツールとして登場しました。しかしIoT時代以前は、一般的なRDBかRRDtoolで事足りるデータ量でした。

2010年代に入りIoTとクラウドモニタリングの普及で時系列データの量が爆発的に増大。2013年にOpenTSDB(HBaseベース)、2014年にInfluxDB(GoおよびRustで実装、スタンドアロンTSDB)が登場し、専用時系列DBの市場が生まれました。2019年には既存のPostgreSQLをTSDB拡張する「TimescaleDB」が普及し、RDBとTSDBの融合という方向性も示されました。

クラウドベンダーも時系列DBをマネージドサービスとして提供するようになり、2018年にAWS Timestream(プレビュー)、2020年にGAとなりました。Azure Time Series Insightsも同時期に強化されています。

産業IoT分野では、OPC-UAとの親和性が高い「Apache IoTDB」が中国を中心に普及しています。

技術仕様

主要な時系列DBの比較

DB実装言語ライセンス特徴
InfluxDBGo/RustBSL(OSSコア)最もポピュラー、InfluxQL/Flux対応
TimescaleDBC(PostgreSQL拡張)Apache 2.0SQLが使える、PostgreSQL互換
PrometheusGoApache 2.0Pull型収集、Kubernetes向け
Victoria MetricsGoApache 2.0高パフォーマンス、PromQL互換
Amazon TimestreamマネージドAWS独自AWSネイティブ統合
Azure Time Series InsightsマネージドAzure独自PaaS、Azureネイティブ
QuestDBJava/C++Apache 2.0超高速(PostgreSQLプロトコル互換)
Apache IoTDBJavaApache 2.0産業IoT、OPC-UA連携
OpenTSDBJavaLGPLHBase上に構築、大規模向け

InfluxDB のデータモデル

Measurement(テーブルに相当)
 └── Tag Set(インデックス付きメタデータ)
      └── Field Set(実際の測定値、インデックスなし)
           └── Timestamp(ナノ秒精度)

例:
measurement: "factory_sensor"
tags:        device_id="cnc-001", location="tokyo", line="A"
fields:      temperature=25.3, vibration=0.42, rpm=3000
timestamp:   1700000000000000000  (Unix nanoseconds)

InfluxDB Line Protocol(書き込みフォーマット):

# 書式: measurement,tag_key=tag_value field_key=field_value timestamp
factory_sensor,device_id=cnc-001,location=tokyo temperature=25.3,vibration=0.42,rpm=3000i 1700000000000000000
factory_sensor,device_id=cnc-002,location=osaka temperature=24.1,vibration=0.31,rpm=2800i 1700000000000000000

データ圧縮の仕組み

時系列DBが高い圧縮率を実現する主な手法:

圧縮手法説明適用例
Delta of Delta差分の差分を符号化タイムスタンプ(規則的な増加)
Gorilla encodingIEEE 754浮動小数点のXOR + 可変長ビットセンサー値(緩やかな変化)
Zigzag encoding符号付き整数を符号なしに変換正負混在の差分値
Run Length Encoding同じ値の連続を圧縮固定値(設定変更がない状態)
辞書圧縮繰り返し文字列を辞書化タグ値(device_idなど)

典型的なIoTセンサーデータでは、これらの組み合わせにより非圧縮の10〜50分の1のサイズに圧縮できます。

動作原理

書き込みパスとLSM-Tree

InfluxDBなど多くのTSDBは、LSM-Tree(Log-Structured Merge-Tree)アーキテクチャを採用しています。

書き込みパス:
  データ受信

  WAL(Write-Ahead Log)に追記

  メモリキャッシュ(MemTable)に保持
    ↓ [定期的フラッシュ]
  TSMファイル(Time-Structured Merge Tree)
    → ディスク上で圧縮・マージ(Compaction)
    → 古いデータほど大きなファイルに統合

読み取りパス:
  クエリ受信

  メモリキャッシュ + TSMファイルを並列読み取り

  マージ・フィルタリング

  結果返却

Flux クエリ言語の例(InfluxDB 2.x)

# InfluxDBのFlux言語: 過去1時間の各デバイスの平均温度
from(bucket: "factory_data")
  |> range(start: -1h)
  |> filter(fn: (r) => r._measurement == "factory_sensor" and r._field == "temperature")
  |> group(columns: ["device_id"])
  |> mean()
  |> yield(name: "average_temperature")

# 1分ごとのダウンサンプリング(大量データのグラフ表示向け)
from(bucket: "factory_data")
  |> range(start: -24h)
  |> filter(fn: (r) => r._measurement == "factory_sensor" and r._field == "vibration")
  |> aggregateWindow(every: 1m, fn: mean, createEmpty: false)
  |> yield(name: "downsampled_vibration")

# 異常検知: 移動平均から3σを超えるデータを検出
data = from(bucket: "factory_data")
  |> range(start: -6h)
  |> filter(fn: (r) => r._measurement == "factory_sensor" and r._field == "vibration")

mean = data |> mean()
stddev = data |> stddev()

data
  |> map(fn: (r) => ({r with zscore: (r._value - mean._value) / stddev._value}))
  |> filter(fn: (r) => math.abs(x: r.zscore) > 3.0)
  |> yield(name: "anomalies")

IoTデバイスからInfluxDBへのデータ送信

# MQTTからInfluxDBへのブリッジ(Python)
import paho.mqtt.client as mqtt
from influxdb_client import InfluxDBClient, Point
from influxdb_client.client.write_api import SYNCHRONOUS
import json
import time

INFLUXDB_URL = "http://localhost:8086"
INFLUXDB_TOKEN = "my-auth-token"
INFLUXDB_ORG = "my-org"
INFLUXDB_BUCKET = "factory_data"

influx_client = InfluxDBClient(url=INFLUXDB_URL, token=INFLUXDB_TOKEN, org=INFLUXDB_ORG)
write_api = influx_client.write_api(write_options=SYNCHRONOUS)

def on_mqtt_message(client, userdata, message):
    """MQTTメッセージを受信してInfluxDBに書き込む"""
    try:
        payload = json.loads(message.payload)
        # トピックからdevice_idを抽出 (例: factory/cnc-001/telemetry)
        parts = message.topic.split('/')
        device_id = parts[1] if len(parts) > 1 else "unknown"

        # InfluxDB Pointを構築
        point = (Point("factory_sensor")
                 .tag("device_id", device_id)
                 .tag("location", payload.get("location", "unknown"))
                 .field("temperature", float(payload.get("temperature", 0)))
                 .field("vibration", float(payload.get("vibration", 0)))
                 .field("rpm", int(payload.get("rpm", 0)))
                 .time(payload.get("timestamp", int(time.time_ns()))))

        write_api.write(bucket=INFLUXDB_BUCKET, record=point)
    except Exception as e:
        print(f"InfluxDB書き込みエラー: {e}")

mqtt_client = mqtt.Client()
mqtt_client.on_message = on_mqtt_message
mqtt_client.connect("mqtt-broker", 1883)
mqtt_client.subscribe("factory/+/telemetry")
mqtt_client.loop_forever()

用途・ユースケース

IoTセンサーデータの保存と可視化

工場の数百台のセンサーから毎秒データを収集し、InfluxDB + Grafanaのスタックでリアルタイムに可視化します。異常値の検出アラートもGrafanaのAlertingで設定可能です。

インフラモニタリング(DevOps)

サーバー・コンテナのCPU/メモリ/ネットワーク使用率を時系列DBで管理。Prometheus + Grafanaが標準的なスタックとして普及しています。

エネルギー管理

スマートメーターや太陽光パネルの発電量データを時系列DBで長期保存。月次・年次の消費トレンド分析や、天気との相関分析に活用します。

金融・トレーディング

株価・FXのティックデータを時系列DBで管理。ミリ秒以下の精度での時刻管理とバックテストが重要です。QuestDBのような超高性能TSDBが使われます。

-- TimescaleDB(PostgreSQL拡張)を使ったIoTデータ管理
-- PostgreSQLの構文が使えるため、既存のSQLスキルが活用できる

-- 時系列テーブルの作成
CREATE TABLE sensor_data (
    time        TIMESTAMPTZ NOT NULL,
    device_id   TEXT NOT NULL,
    temperature DOUBLE PRECISION,
    vibration   DOUBLE PRECISION,
    rpm         INTEGER
);

-- TimescaleDBのハイパーテーブルに変換(時系列最適化)
SELECT create_hypertable('sensor_data', 'time');

-- データ保持ポリシー(30日以上前のデータを自動削除)
SELECT add_retention_policy('sensor_data', INTERVAL '30 days');

-- 連続集計(バックグラウンドで1分ごとの集計を維持)
CREATE MATERIALIZED VIEW sensor_1min
WITH (timescaledb.continuous) AS
SELECT
    time_bucket('1 minute', time) AS bucket,
    device_id,
    avg(temperature) AS avg_temp,
    max(vibration) AS max_vibration
FROM sensor_data
GROUP BY bucket, device_id;

-- 効率的な時間範囲クエリ
SELECT time, device_id, temperature
FROM sensor_data
WHERE time > NOW() - INTERVAL '1 hour'
  AND device_id = 'cnc-001'
  AND temperature > 80
ORDER BY time DESC;

実装・開発のポイント

データ設計のベストプラクティス

InfluxDBのタグとフィールドの使い分け:

タグ(Tags)← インデックス化される → クエリのWHERE条件に使う
  ✓ device_id、location、sensor_type
  ✗ 温度値、振動値(高カーディナリティになりメモリを大量消費)

フィールド(Fields)← インデックスなし → 実際の測定値
  ✓ temperature, vibration, rpm
  ✗ device_id(フィールドにするとインデックスなしでクエリが遅い)

データ保持戦略(階層的保存)

生データ(1秒間隔)    → 保持期間: 7日
1分ダウンサンプル     → 保持期間: 90日
1時間ダウンサンプル   → 保持期間: 2年
1日ダウンサンプル     → 保持期間: 10年
# InfluxDB: 自動ダウンサンプリングタスク設定
task_script = """
option task = {
    name: "downsample_1min",
    every: 1m,
}

from(bucket: "raw_data")
  |> range(start: -task.every)
  |> filter(fn: (r) => r._measurement == "factory_sensor")
  |> aggregateWindow(every: 1m, fn: mean)
  |> to(bucket: "downsampled_1min", org: "my-org")
"""

他技術との比較

項目InfluxDBTimescaleDBAmazon TimestreamRDB(PostgreSQL)
時系列特化完全特化拡張で対応完全特化汎用
SQLサポート独自言語(Flux)、InfluxQLフルSQL独自SQLフルSQL
スケーラビリティ非常に高(マネージド)
圧縮率非常に高
運用負担中(PG管理)低(マネージド)
既存スキル活用要学習SQL使える要学習そのまま
ライセンスBSL(制限あり)Apache 2.0有料PostgreSQLライセンス

時系列データベースはIoTプラットフォームから収集されたセンサーデータの永続化に不可欠であり、ダッシュボードツール(Grafanaなど)のデータソースとして機能します。エッジコンピューティング環境では、軽量な時系列DB(InfluxDB OSS、QuestDB)をエッジノード上で動かすことで、クラウドへの通信量を削減しつつリアルタイム分析を実現します。

関連用語

参考リンク