こんにちは。SJC共同開発推進室の菊池です。
弊社はIoT関連の開発を多く手がけていることもあり、センサーから上がってくる大量の時系列データを保存するケースが多々あります。
今回、その保存先としてPostgreSQLを利用しようと考えており、大量の時系列データを効率よく管理できる方法を探していたところ、
「TimescaleDB」という拡張機能があることを知りましたのでご紹介します。
「TimescaleDB」導入の経緯
大量の時系列データを扱うならS3に置くべきか、いっそのことNoSQLにしてしまうべきか、色々と検討しましたが、
すでにマスタデータの管理などでPostgreSQLを使っており、既存のアプリケーションもそれに合わせて動いています。
諸々状況を考慮した結果、インフラコストの観点を最優先とし、現状のシステム構成に手を加えずに済むPostgreSQLでの保存を選択しました。
しかし懸念となるのが、大量の時系列データを扱う場合のパフォーマンスです。
もちろんインデックスを張るだけでもかなりの効果は期待できますが、今回はさらに上のパフォーマンスを目指したい。
その手段として行き着いたのが「パーティショニング」の導入でした。
パーティショニングとは、1つの巨大なテーブルを日付などの条件で複数の小さなテーブルに分割し、必要なデータだけを効率よく扱うためのPostgreSQLの標準機能です。
例えば、以下のように設定することで、日付ごとにデータをパーティショニングできます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
-- ① 親テーブル(ここで全体のデータ構造・データ型・主キーを定義する) CREATE TABLE sensor_data ( time TIMESTAMPTZ(3) NOT NULL, -- データの計測日時 device_id VARCHAR(50) NOT NULL, -- センサー機器の識別ID val DOUBLE PRECISION NOT NULL, -- 実際の計測値 PRIMARY KEY (time, device_id) -- 分割キー(time)を含めた複合主キー ) PARTITION BY RANGE (time); -- ★時間ごとにパーティション -- ② 子テーブル(実際の保存先。日付ごとに手動であらかじめ作成しておく必要がある) CREATE TABLE sensor_data_20260601 PARTITION OF sensor_data FOR VALUES FROM ('2026-06-01 00:00:00+09') TO ('2026-06-02 00:00:00+09'); CREATE TABLE sensor_data_20260602 PARTITION OF sensor_data FOR VALUES FROM ('2026-06-02 00:00:00+09') TO ('2026-06-03 00:00:00+09'); |
このようにあらかじめデータを時間で分割しておくと、特定の期間を検索する際、
関係のない子テーブルへの無駄なディスクI/Oを完全に排除してSELECTできるため、処理が高速化するという仕組みです。
ですが、実際にシステムへ導入する場合、
- パーティションを毎回手動で追加する
-
自動作成するバッチを自前で組んで運用する
など、パーティショニングを維持するために自前で管理し続けなければならないという課題があります。
もし、不具合等でバッチが途中で停止したり、手動での追加を忘れたりした状態で、まだ作成していない日付のデータが流れ込むと、
DBエラーが発生してサービス全体が停止するリスクもあります…
TimescaleDBなら「データが来た瞬間にその場で自動生成」
TimescaleDBは、この「未来のパーティションテーブルを事前に手動で追加しなければいけない」という処理を自動で行ってくれるのが大きな特徴です
WSL(Ubuntu)環境を用いて、実際にTimescaleDBの環境を構築していきます。
1. インストールと初期設定
公式サイトを参考に、リポジトリの追加とインストールを行います。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# TimescaleDBの公式リポジトリを追加 echo "deb https://packagecloud.io/timescale/timescaledb/ubuntu/ $(lsb_release -c -s) main" | sudo tee /etc/apt/sources.list.d/timescaledb.list wget --quiet -O - /etc/apt/trusted.gpg.d/timescaledb.gpg https://packagecloud.io/timescale/timescaledb/gpgkey | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/timescaledb.gpg # インストール sudo apt update sudo apt install timescaledb-2-postgresql-18 postgresql-client-18 # 自動チューニングの実行と起動 sudo timescaledb-tune sudo systemctl restart postgresql |
2. 拡張機能の有効化とテーブル作成
データベースにログインして、初期設定を行います。
|
1 2 3 4 5 6 7 8 9 10 11 |
<br /># postgresユーザーのパスワードを設定(一度psqlに入って設定します) sudo -u postgres psql # 【psql内で実行】パスワードを入力・確定したら、\q で一度 psql を終了します \password postgres \q # 設定したパスワードを使って、データベースに再ログイン psql -d "postgres://postgres:設定したパスワード@localhost:5432/postgres" |
拡張機能を有効化し、「自動パーティショニング」機能を扱うためのテーブル「ハイパーテーブル(Hypertable)」を作成します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
-- 拡張機能の有効化 CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE; -- テスト用テーブルを作成 CREATE TABLE sensor_data_hyper ( time TIMESTAMPTZ(3) NOT NULL, device_id VARCHAR(50) NOT NULL, val DOUBLE PRECISION NOT NULL, PRIMARY KEY (time, device_id) ); -- ★ハイパーテーブル(Hypertable)の作成 -- ※ここでは「最大1日幅」で自動分割されるように設定します。 SELECT create_hypertable('sensor_data_hyper', 'time', chunk_time_interval => INTERVAL '1 day'); |
例として30日間にまたがるテストデータを投入してみます。
|
1 2 3 4 5 6 7 8 9 |
-- 30日分(1,000万件)の疑似データを一気に投入 INSERT INTO sensor_data_hyper (time, device_id, val) SELECT '2026-06-01 00:00:00+09'::timestamptz + (gs * INTERVAL '250 milliseconds'), 'dev-' || lpad(((gs % 50) + 1)::text, 3, '0'), random() * 100 FROM generate_series(1, 10000000) AS gs; |
この時、データがINSERTされたタイミングで、必要に応じて「チャンク(Chunk)」と呼ばれる物理テーブルが自動生成されます。
チャンクとは?
ハイパーテーブルの裏側で、TimescaleDBが自動生成する物理テーブルです。これが自動パーティションの実体となります。
参考:Hypertables and chunks
本当に裏側でチャンクが自動生成されているかは、TimescaleDB専用の show_chunks() 関数 を使うことで確認できます。
|
1 2 3 4 |
-- 生成されたパーティション(チャンク)の一覧を確認するクエリ SELECT show_chunks('sensor_data_hyper'); |
実行結果:
|
1 2 3 4 5 6 7 8 9 10 |
show_chunks ----------------------------------------- _timescaledb_internal._hyper_3_35_chunk _timescaledb_internal._hyper_3_36_chunk _timescaledb_internal._hyper_3_37_chunk _timescaledb_internal._hyper_3_38_chunk ..... < 省略> ..... (30 rows) |
実際にデータを取得するときのSELECTは以下のように、「ハイパーテーブル」に対して、通常のPostgreSQLと全く同じようにクエリを投げるだけでOKです。
|
1 2 3 4 5 6 |
-- 特定の日付・デバイスのデータを検索 SELECT * FROM sensor_data_hyper WHERE time >= '2026-06-15 00:00:00+09' AND time < '2026-06-16 00:00:00+09' AND device_id = 'dev-001'; |
実行結果:
|
1 2 3 4 5 6 7 8 9 10 11 |
<br />time | device_id | val --------------------------+-----------+---------------------- 2026-06-15 00:00:00+09 | dev-001 | 44.58871285602805 2026-06-15 00:00:12.5+09 | dev-001 | 29.060373821845342 2026-06-15 00:00:25+09 | dev-001 | 97.91692426048535 2026-06-15 00:00:37.5+09 | dev-001 | 97.88585982700857 2026-06-15 00:00:50+09 | dev-001 | 53.61012133840974 ..... < 省略> ..... |
まとめ
今回はTimescaleDBの構築をメインに解説しました。
実際に触ってみると意外と簡単に設定でき、面倒なパーティション設定を自動で行ってくれる点は、
自前でパーティション設定するよりも非常に大きなメリットだと感じます。
(ただ、26年6月現在、Amazon RDSのPostgreSQLではTimescaleDBを使うことができないのが残念…)
次回は、実際にアプリケーションからどのように操作するかを検証してみたいと思います!
最後に
エコモットでは一緒にモノづくりをしていく仲間を募集中です。
弊社に少しでも興味がある方は、ぜひ下記の採用ページをご覧ください!




