- Authors
- Name

1.入り
IoTセンサー、アプリケーションメトリック、金融価格、サーバーログなどの時系列データは、現代のインフラストラクチャで最も急成長しているデータタイプです。これらのデータは一般的に時間軸に基づいて連続的に生成され、INSERTは大量ですがUPDATEはほとんどなく、最近のデータに対する照会が圧倒的に多いという特性を持っています。
PostgreSQLは汎用リレーショナルデータベースとして時系列データを格納できますが、数十億行規模で時間範囲クエリのパフォーマンスが急激に低下します。ネイティブパーティショニングを使用してもパーティション管理、圧縮、集計最適化を直接実装しなければならない運営負担が大きい。
TimescaleDBはこの問題を正面に解決します。 PostgreSQLの拡張(extension)で動作しながら、Hypertableという自動パーティショニングメカニズム、連続集計(Continuous Aggregates)、ネイティブ圧縮、自動データ保存ポリシーなどを提供する。既存のPostgreSQLクエリ、インデックス、結合、トランザクションをそのまま使用しながらも、時系列ワークロードに特化したパフォーマンスを得ることができる点が最大の利点だ。
この記事では、TimescaleDBのアーキテクチャからHypertable設計、連続集約、圧縮最適化、データ保存ポリシー、マルチノード構成、監視、トラブルシューティングまで、本番運用に必要なすべての内容について説明します。
2. TimescaleDBアーキテクチャ:HypertableとChunk
Hypertableの構造
TimescaleDBの中心はHypertableです。ユーザーは単一のテーブルに見えますが、内部的には時間軸に基づいて自動的に分割されたいくつかのチャンクで構成されています。各チャンクはPostgreSQLの一般テーブルであり、指定された時間間隔(基本7日)に対応するデータを格納します。``` 사용자 관점: ┌───────────────────────────────────────────┐ │ sensor_data (Hypertable) │ │ SELECT * FROM sensor_data │ │ WHERE time > now() - interval '1 hour' │ └───────────────────────────────────────────┘
내부 구조: ┌────────────────┬────────────────┬────────────────┬────────────────┐ │ _hyper_1_1 │ _hyper_1_2 │ _hyper_1_3 │ _hyper_1_4 │ │ (2026-02-15 │ (2026-02-22 │ (2026-03-01 │ (2026-03-07 │ │ ~02-21) │ ~02-28) │ ~03-06) │ ~현재) │ │ [압축됨] │ [압축됨] │ [압축 예정] │ [활성] │ └────────────────┴────────────────┴────────────────┴────────────────┘
- **自動パーティショニング**:新しいデータが到着すると、適切なチャンクが自動的に作成されます。手動パーティション管理は不要です。
- **クエリ最適化**:時間範囲条件を持つクエリは、対応するチャンクのみをスキャンします。不要なチャンクは最初に近づかない。
- **独立管理**: 各チャンクは独立して圧縮、削除、移動が可能。古いデータを圧縮しながら、最新のデータは高速INSERTを維持できます。
- **インデックス効率**:各チャンクは別々のB-treeインデックスを持つため、テーブルサイズ全体ではなくチャンクサイズに比例したインデックスを保持します。
### チャンク間隔(Chunk Interval)選択基準
チャンク間隔は、TimescaleDBのパフォーマンスに直接影響を与える重要なパラメータです。アクティブチャンク(現在書き込みが行われているチャンク)のインデックスがメモリに常駐していると、最適なINSERT性能が得られます。
**推奨基準**:アクティブチャンク1つのサイズが利用可能メモリの25%以下になるように間隔を設定します。
|毎日のデータ量メモリ|推奨チャンク間隔|アクティブチャンク予想サイズ|
| ------------- | ------ | -------------- | ------------------- |
| 100MB | 4GB | 7日| 〜700MB |
| 1GB | 8GB | 1日| ~1GB |
| 10GB | 32GB | 6時間| 〜2.5GB |
| 100GB | 64GB | 1時間| ~4.2GB |
## 3. インストールと初期設定
### PostgreSQLにTimescaleDBをインストールする
TimescaleDBはPostgreSQL 14〜17をサポートしています。 2026年基準PostgreSQL 16または17との組み合わせが最も安定している。```bash
# Ubuntu/Debian 기준 설치
sudo apt install gnupg postgresql-common apt-transport-https lsb-release wget
# 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 - https://packagecloud.io/timescale/timescaledb/gpgkey | sudo apt-key add -
sudo apt update
# PostgreSQL 17용 TimescaleDB 설치
sudo apt install timescaledb-2-postgresql-17
# 자동 튜닝 실행 (postgresql.conf 최적화)
sudo timescaledb-tune --yes
# PostgreSQL 재시작
sudo systemctl restart postgresql
timescaledb-tune銀システムメモリ、CPUコア数などを分析してshared_buffers、work_mem、effective_cache_size、max_worker_processesなどを自動的に調整する。特にmax_worker_processesはチャンクごとの並列処理に影響を与えるので、十分に確保しなければならない。```sql -- 데이터베이스에 확장 활성화 CREATE EXTENSION IF NOT EXISTS timescaledb;
-- 설치 확인 SELECT extversion FROM pg_extension WHERE extname = 'timescaledb'; -- 결과: 2.17.2 (2026년 3월 기준 최신) ###コアpostgresql.confの設定ini
timescaledb-tune이 자동 설정하지만 수동 확인이 필요한 항목
shared_preload_libraries = 'timescaledb' max_worker_processes = 32 # 병렬 쿼리 + 백그라운드 워커 max_parallel_workers_per_gather = 4 max_parallel_workers = 16 timescaledb.max_background_workers = 16 # 압축, 리오더 등 백그라운드 작업
시계열 워크로드 최적화
random_page_cost = 1.1 # SSD 기준 effective_io_concurrency = 200 # SSD 기준 checkpoint_completion_target = 0.9 wal_buffers = 64MB
### 基本 Hypertable の生成```sql
-- 일반 테이블 생성 (PostgreSQL 표준)
CREATE TABLE sensor_data (
time TIMESTAMPTZ NOT NULL,
sensor_id INTEGER NOT NULL,
location TEXT NOT NULL,
temperature DOUBLE PRECISION,
humidity DOUBLE PRECISION,
pressure DOUBLE PRECISION,
battery DOUBLE PRECISION
);
-- Hypertable로 변환
SELECT create_hypertable(
'sensor_data', -- 테이블 이름
'time', -- 시간 컬럼
chunk_time_interval => INTERVAL '1 day', -- 청크 간격
if_not_exists => TRUE
);
-- 다차원 파티셔닝 (시간 + 공간)
-- 센서 수가 매우 많고 센서별 조회가 빈번한 경우 유용
SELECT create_hypertable(
'sensor_data',
'time',
chunk_time_interval => INTERVAL '1 day',
partitioning_column => 'sensor_id', -- 공간 차원 추가
number_partitions => 4 -- 해시 파티션 수
);
```多次元パーティショニング`sensor_id = 42 AND time > ...`型のクエリで追加のチャンク除外を可能にします。ただし、パーティション数をあまりに多く設定するとチャンク数が過度に増え、管理オーバーヘッドが増加するので注意しなければならない。
###インデックス戦略
TimescaleDBは、Hypertableの作成時に時間列のBツリーインデックスを自動的に生成します。追加のインデックスはクエリパターンに合わせて設計されています。```sql
-- 센서별 + 시간 복합 인덱스 (가장 빈번한 쿼리 패턴)
CREATE INDEX idx_sensor_time ON sensor_data (sensor_id, time DESC);
-- 위치별 조회가 빈번한 경우
CREATE INDEX idx_location_time ON sensor_data (location, time DESC);
-- 최근 데이터 위주의 부분 인덱스 (인덱스 크기 절약)
CREATE INDEX idx_recent_temp ON sensor_data (sensor_id, time DESC)
WHERE time > now() - INTERVAL '30 days';
```部分インデックスは時系列データで特に有効です。ほとんどのクエリは最近のデータを照会するため、古いデータにはインデックスを保持する必要はありません。
## 5. 連続集計(Continuous Aggregates)
###コンセプトと必要性
時系列データ分析で最も一般的なパターンは時間単位の集計です。 「過去7日間の時間別平均温度」、「月別センサー稼働率」などのクエリを元のデータに直接実行すると、数億行をスキャンする必要があります。
Continuous Aggregatesは、この問題を解決するTimescaleDBの重要な機能です。 PostgreSQLのMaterialized Viewと似ていますが、2つの決定的な違いがあります。
- **増分更新**: テーブル全体を再集計せず、最後の更新以降に変更されたデータのみを処理する。
- **リアルタイム結合**: 集計されたデータとまだ集計されていない最新データをクエリ時点に自動的に結合し、常に最新の結果を返す。
### 連続集計の作成とポリシーの設定```sql
-- 시간별 집계 Continuous Aggregate 생성
CREATE MATERIALIZED VIEW sensor_hourly
WITH (timescaledb.continuous) AS
SELECT
time_bucket('1 hour', time) AS bucket,
sensor_id,
location,
AVG(temperature) AS avg_temp,
MIN(temperature) AS min_temp,
MAX(temperature) AS max_temp,
AVG(humidity) AS avg_humidity,
COUNT(*) AS sample_count
FROM sensor_data
GROUP BY bucket, sensor_id, location
WITH NO DATA; -- 생성 시 즉시 집계하지 않음
-- 자동 갱신 정책 설정
-- 1시간마다 실행, 최근 3시간 데이터를 갱신 대상으로 설정
SELECT add_continuous_aggregate_policy('sensor_hourly',
start_offset => INTERVAL '3 hours', -- 얼마나 과거부터 갱신할 것인가
end_offset => INTERVAL '1 hour', -- 얼마나 최근까지 갱신할 것인가
schedule_interval => INTERVAL '1 hour' -- 갱신 주기
);
-- 일별 집계 (시간별 집계 위에 계층적으로 구성 가능)
CREATE MATERIALIZED VIEW sensor_daily
WITH (timescaledb.continuous) AS
SELECT
time_bucket('1 day', bucket) AS bucket,
sensor_id,
location,
AVG(avg_temp) AS avg_temp,
MIN(min_temp) AS min_temp,
MAX(max_temp) AS max_temp,
SUM(sample_count) AS sample_count
FROM sensor_hourly
GROUP BY time_bucket('1 day', bucket), sensor_id, location
WITH NO DATA;
SELECT add_continuous_aggregate_policy('sensor_daily',
start_offset => INTERVAL '3 days',
end_offset => INTERVAL '1 day',
schedule_interval => INTERVAL '1 day'
);
```**階層的連続集計(Hierarchical Continuous Aggregates)**は、TimescaleDB 2.9+でサポートされています。元のHypertableで時間別集計を作成し、時間別集計で日別集計を作成すると、計算コストが大幅に削減されます。元のデータ数十億行の代わりに時間別集計数万行から日別集計を生成するものである。
###リアルタイムモードと非リアルタイムモード```sql
-- 실시간 모드 (기본값) - 항상 최신 데이터 반영
ALTER MATERIALIZED VIEW sensor_hourly
SET (timescaledb.materialized_only = false);
-- 비실시간 모드 - 마지막 갱신 시점까지의 데이터만 반환 (더 빠름)
ALTER MATERIALIZED VIEW sensor_hourly
SET (timescaledb.materialized_only = true);
```リアルタイムモードは、集計されていない最新のデータをクエリの時点でオンザフライで集計するため、多少のパフォーマンスコストがあります。ダッシュボードから1分以内の精度が必要ない場合は、非リアルタイムモードがより効率的です。
## 6. ネイティブ圧縮
### 圧縮アーキテクチャ
TimescaleDBのネイティブ圧縮はチャンク単位で動作します。圧縮チャンクは行指向記憶から列指向記憶に変換され、データタイプに応じて最適な圧縮アルゴリズムが自動的に適用されます。
**圧縮効果**: 一般的な時系列データで90~95%のストレージ節約が可能。これは元の100GBデータが5〜10GBに減るレベルです。```sql
-- 압축 설정 활성화
ALTER TABLE sensor_data SET (
timescaledb.compress,
timescaledb.compress_segmentby = 'sensor_id, location', -- 세그먼트 기준
timescaledb.compress_orderby = 'time DESC' -- 정렬 기준
);
-- 자동 압축 정책: 7일 이상 된 청크를 자동 압축
SELECT add_compression_policy('sensor_data', INTERVAL '7 days');
-- 수동 압축 (특정 청크 즉시 압축)
SELECT compress_chunk(c.chunk_name)
FROM timescaledb_information.chunks c
WHERE c.hypertable_name = 'sensor_data'
AND c.range_end < now() - INTERVAL '7 days'
AND NOT c.is_compressed;
-- 압축 상태 확인
SELECT
chunk_name,
range_start,
range_end,
is_compressed,
pg_size_pretty(before_compression_total_bytes) AS before_size,
pg_size_pretty(after_compression_total_bytes) AS after_size,
ROUND(
(1 - after_compression_total_bytes::numeric
/ NULLIF(before_compression_total_bytes, 0)) * 100, 1
) AS compression_ratio_pct
FROM timescaledb_information.compressed_chunk_stats
WHERE hypertable_name = 'sensor_data'
ORDER BY range_end DESC
LIMIT 10;
```### compress_segmentby デザイン`compress_segmentby`は圧縮性能の鍵です。この列は、圧縮チャンク内でデータをグループ化するための基準です。圧縮データに対するクエリ`WHERE sensor_id = 42`同様にセグメントキーでフィルタリングすると、そのセグメントだけを解除すればよいので、クエリ性能が大幅に向上する。
**設計原則**:
-`segmentby`には、WHERE条件やGROUP BYによく使われる列を指定する。
- カーディナリティが高すぎる列(例:数百万のuser_id)は避けてください。セグメント数が過度に増加し、圧縮率が低下する。
- カーディナリティが低すぎるカラム(例えば、statusが3つ)も単独では非効率的である。
- 一般に、数百から数万の固有値を持つ列が適しています。
## 7. データ保存ポリシー(Retention Policy)
時系列データは時間の経過とともに価値が減少することが多い。古い元のデータは削除されますが、集計されたデータはより長く保存する階層保存戦略が有効です。```sql
-- 90일 이상 된 원본 데이터 자동 삭제
SELECT add_retention_policy('sensor_data', INTERVAL '90 days');
-- 시간별 집계는 1년 보존
SELECT add_retention_policy('sensor_hourly', INTERVAL '1 year');
-- 일별 집계는 3년 보존
SELECT add_retention_policy('sensor_daily', INTERVAL '3 years');
-- 보존 정책 확인
SELECT * FROM timescaledb_information.jobs
WHERE proc_name = 'policy_retention';
-- 수동으로 특정 시점 이전 데이터 즉시 삭제
SELECT drop_chunks('sensor_data', older_than => INTERVAL '90 days');
```この階層的保存戦略の効果をまとめると、次のようになる。
|データ層保存期間|解像度主な用途|
| ----------- | --------- | --------- | ----------------------- |
|元のデータ| 90日|秒単位|リアルタイムモニタリング、デバッグ|
|時間別集計1年|時間単位|トレンド分析、ダッシュボード|
|毎日の集計3年|日単位|長期レポート、容量計画|
## 8. クエリの最適化
### 時系列専用関数の活用
TimescaleDBは、時系列分析に特化したハイパー関数を提供します。```sql
-- time_bucket: 시간 단위 그룹화 (가장 핵심적인 함수)
SELECT
time_bucket('15 minutes', time) AS bucket,
sensor_id,
AVG(temperature) AS avg_temp,
percentile_cont(0.95) WITHIN GROUP (ORDER BY temperature) AS p95_temp
FROM sensor_data
WHERE time > now() - INTERVAL '6 hours'
AND sensor_id = 42
GROUP BY bucket, sensor_id
ORDER BY bucket DESC;
-- time_bucket_gapfill: 데이터가 없는 시간대도 채워서 반환
SELECT
time_bucket_gapfill('1 hour', time) AS bucket,
sensor_id,
AVG(temperature) AS avg_temp,
COALESCE(AVG(temperature), locf(AVG(temperature))) AS filled_temp
FROM sensor_data
WHERE time > now() - INTERVAL '24 hours'
AND sensor_id = 42
GROUP BY bucket, sensor_id
ORDER BY bucket;
-- first/last: 시간 기준 첫 번째/마지막 값
SELECT
time_bucket('1 hour', time) AS bucket,
sensor_id,
first(temperature, time) AS opening_temp,
last(temperature, time) AS closing_temp,
MAX(temperature) - MIN(temperature) AS temp_range
FROM sensor_data
WHERE time > now() - INTERVAL '24 hours'
GROUP BY bucket, sensor_id;
```###クエリパフォーマンス最適化チェックリスト
- **時間範囲条件を必ず含めてください**:`WHERE time > now() - INTERVAL '1 day'`なしでテーブル全体をスキャンすると、チャンク除外は機能しません。
- **セグメントキーフィルタを一緒に使用する**:圧縮データから`WHERE sensor_id = 42`を追加すると、そのセグメントのみが解放されます。
- **EXPLAIN ANALYZEでチャンク除外を確認する**:実行計画でスキャンされたチャンクの数が予想より多い場合は、クエリ条件またはチャンク間隔を見直します。
- **連続集計を活用する**:ダッシュボードクエリは、可能であれば元のデータの代わりに連続集計を照会するように設計されています。```sql
-- 쿼리 실행 계획에서 청크 제외 확인
EXPLAIN ANALYZE
SELECT AVG(temperature)
FROM sensor_data
WHERE time > now() - INTERVAL '2 hours'
AND sensor_id = 42;
-- 결과에서 "Chunks excluded" 항목 확인
-- Append (actual rows=...)
-- -> Index Scan on _hyper_1_42_chunk (actual rows=...)
-- Index Cond: (time > ...)
-- -> (N chunks excluded) <-- 이 부분이 핵심
```## 9. マルチノード構成
TimescaleDBは単一ノードでも数TBの時系列データを処理できますが、より大きな規模ではマルチノード構成が必要です。 2025年以来、TimescaleDBはマルチノードアーキテクチャをTimescale Cloudのサービスベースの拡張に変える方向に進んでいます。
セルフホスティング環境での水平拡張戦略は次のとおりです。
- **読み取り拡張**:PostgreSQLのストリーミングレプリケーションを活用して読み取り専用レプリカを追加します。ダッシュボードクエリをレプリカに分散します。
- **書き込み拡張**:センサーIDや地域などの論理基準にデータを分割し、複数のTimescaleDBインスタンスに分散します。アプリケーションレベルでルーティングするか、Citusと組み合わせる方法があります。
- **Timescale Cloud**:マネージドサービスを使用すると、動的リソーススケーリング、自動複製、ポイントインタイムリカバリなどが組み込まれています。
## 10. モニタリング
### コア監視クエリ```sql
-- 1. Hypertable별 전체 크기 확인
SELECT
hypertable_name,
pg_size_pretty(hypertable_size(format('%I.%I', hypertable_schema, hypertable_name)::regclass)) AS total_size,
pg_size_pretty(pg_total_relation_size(format('%I.%I', hypertable_schema, hypertable_name)::regclass)
- pg_relation_size(format('%I.%I', hypertable_schema, hypertable_name)::regclass)) AS index_size,
num_chunks,
compression_enabled
FROM timescaledb_information.hypertables
ORDER BY hypertable_size(format('%I.%I', hypertable_schema, hypertable_name)::regclass) DESC;
-- 2. 청크별 상세 정보 (크기, 압축 여부, 행 수)
SELECT
chunk_name,
range_start,
range_end,
is_compressed,
pg_size_pretty(
pg_total_relation_size(format('%I.%I', chunk_schema, chunk_name)::regclass)
) AS chunk_size
FROM timescaledb_information.chunks
WHERE hypertable_name = 'sensor_data'
ORDER BY range_end DESC
LIMIT 20;
-- 3. 백그라운드 작업(Jobs) 상태 모니터링
SELECT
job_id,
application_name,
proc_name,
hypertable_name,
schedule_interval,
last_run_status,
last_run_started_at,
last_run_duration,
next_start,
total_successes,
total_failures
FROM timescaledb_information.job_stats js
JOIN timescaledb_information.jobs j USING (job_id)
ORDER BY last_run_started_at DESC;
-- 4. 압축 효율 종합 리포트
SELECT
hypertable_name,
pg_size_pretty(SUM(before_compression_total_bytes)) AS before_compression,
pg_size_pretty(SUM(after_compression_total_bytes)) AS after_compression,
ROUND(
(1 - SUM(after_compression_total_bytes)::numeric
/ NULLIF(SUM(before_compression_total_bytes), 0)) * 100, 1
) AS savings_pct,
COUNT(*) AS compressed_chunks
FROM timescaledb_information.compressed_chunk_stats
GROUP BY hypertable_name;
```### Prometheus + Grafana連動
TimescaleDBは`pg_prometheus`アダプタまたはPromscale(現在はdeprecated、その後はPromscaleの機能を吸収したTimescale Cloud)を介してPrometheusメトリックを直接保存することもできます。逆にTimescaleDB自体のメトリックをPrometheusとして収集するには`postgres_exporter`を使用する。
## 11. 時系列データベースの比較
|アイテム| TimescaleDB | InfluxDB(3.x)| ClickHouse |
| ---------------- | ------------------------------ | ------------------------------- | --------------------------------- |
|ベース技術PostgreSQL拡張| Apache Arrow + DataFusion |セルフエンジン(C ++)|
|クエリ言語|標準SQL(完全互換)| SQL(InfluxQLの代替)、Flight SQL | SQL(自己方言)|
| JOINサポート|完全サポート(PostgreSQL機能)|限定|サポート(大型JOIN注意)|
|圧縮率90〜95%(カラム指向変換)|高(Parquetベース)| 90〜95%(カラム指向ネイティブ)|
|連続集計ネイティブサポート(増分更新)|タスクベースの集計|マテリアライズドビュー(INSERTトリガ)|
|データ保存ポリシーネイティブ(自動チャンク削除)|バケットベースの保存| TTL(パーティション/行単位)|
|トランザクションサポート|完全ACID(PostgreSQL)|未サポート未サポート
|学習曲線Low(PostgreSQLユーザーの場合)|中(新しいアーキテクチャの切り替え中)|ミドル(ユニークなコンセプトの多く)|
|エコシステム| PostgreSQL全体(pg拡張、ORM)| Telegraf、Kapacitor独立したエコシステム
|メインユースケース| IoT、メトリック、PostgreSQL拡張| DevOpsメトリック、IoT |大規模分析、ログ
|ライセンス| Apache 2.0(コミュニティ)| MIT / Apache 2.0 | Apache 2.0 |
**選択ガイド**:
- **すでにPostgreSQLを実行していて、SQLの互換性とトランザクションが重要な場合**:TimescaleDBが最善です。別のデータベースをさらに運用する必要はありません。
- **純粋なメトリック/ログ収集パイプラインが重要であり、Telegrafエコシステムを活用したい場合**: InfluxDBが適しています。
- **数十TB以上の分析ワークロードでサブ秒応答が必要な場合**:ClickHouseが最適です。ただし、ACIDトランザクションは放棄しなければならない。
## 12.トラブルシューティング
### 問題 1: INSERT パフォーマンスの低下
**症状**:INSERT遅延時間が徐々に増加します。特にチャンク境界(新しいチャンク生成時点)でスパイクが発生する。
**原因と解決**:
- チャンク間隔が短すぎて頻繁なチャンク生成が発生した場合:チャンク間隔を増やす。
- インデックスが過剰な場合:不要なインデックスを削除します。時系列データにインデックスを1つ追加すると、INSERT性能が10~20%低下することがある。
- WAL書き込みボトルネック:`wal_buffers`、`max_wal_size`を増加させる。
###問題2:圧縮チャンクで遅いクエリ
**症状**:圧縮データを照会するときに非圧縮データよりもむしろ遅い。
**原因と解決**:
-`compress_segmentby`がクエリパターンと一致しない場合:セグメントキーでフィルタリングしないクエリは、すべてのセグメントを解放する必要があります。クエリパターンに合わせて`segmentby`設定を変更する(既存の圧縮データは解放後に再圧縮が必要)。
- 圧縮チャンクについて`ORDER BY`秋`compress_orderby`と異なる場合:位置合わせ方向を合わせます。
###問題3:連続集計更新に失敗しました
**症状**:`timescaledb_information.job_stats`で連続集計作業の`last_run_status`秋`Failed`と表示されます。
**原因と解決**:```sql
-- 실패한 작업 상세 확인
SELECT
job_id,
proc_name,
last_run_status,
last_run_started_at,
last_run_duration,
config
FROM timescaledb_information.job_stats js
JOIN timescaledb_information.jobs j USING (job_id)
WHERE last_run_status = 'Failed';
-- 작업 로그에서 오류 메시지 확인 (PostgreSQL 로그)
-- 일반적인 원인: 메모리 부족(work_mem), 디스크 공간 부족, 락 경합
-- 수동으로 연속 집계 갱신 실행 (디버깅 목적)
CALL refresh_continuous_aggregate('sensor_hourly',
now() - INTERVAL '3 hours',
now() - INTERVAL '1 hour'
);
```### 問題 4: ディスク領域の爆発
**症状**:ディスク使用量が予想よりも速く増加し、サービスに影響を与えます。
**解決手順**:
1.圧縮ポリシーが正常に動作していることを確認してください。
2. 保存ポリシーが設定されていることを確認します。
3. 非圧縮チャンクのうち古いものを手動で圧縮する。
4. 不要な古いチャンクをすぐに削除します。
## 13. 操作時の注意事項
### 圧縮データのDML制限
圧縮されたチャンクにはINSERT、UPDATE、DELETEを直接実行することはできません。圧縮された時間範囲に遅れて到着したデータを処理するには、まずチャンクを解放する必要があります。```sql
-- 특정 청크 압축 해제
SELECT decompress_chunk('_timescaledb_internal._hyper_1_3_chunk');
-- 데이터 INSERT/UPDATE 수행 후 다시 압축
SELECT compress_chunk('_timescaledb_internal._hyper_1_3_chunk');
```遅く到着するデータが頻繁な場合、圧縮対象から最近のN日を除くオフセットを十分に大きく設定しなければならない。
### 移行時の注意事項
- TimescaleDBメジャーバージョンのアップグレード(例:2.x - > 3.x)の場合は、必ず公式の移行ガイドに従います。
- PostgreSQLメジャーバージョンのアップグレードとTimescaleDBのアップグレードは同時にしない。一つずつ順次進行する。
- アップグレード前`pg_dump`でスキーマバックアップを実行し、可能であればテスト環境で最初に検証します。
### 接続プーリング
TimescaleDBは内部的にバックグラウンドワーカーを使用するため、`max_connections`に余裕を置かなければならない。 PgBouncerを使用するときは`transaction` 모드를 사용하고, prepared statement 호환성에 주의한다.
## 14. 실패 사례와 복구
### 사례 1: 청크 간격 미설정으로 인한 성능 저하
한 팀이 기본 청크 간격(7일)을 사용하면서 일 100GB의 센서 데이터를 적재했다. 활성 청크가 700GB에 달해 메모리(32GB)의 20배를 초과했고, INSERT 지연이 수 초까지 증가했다.
**복구**: 청크 간격을 1시간으로 변경하고 기존 데이터를 마이그레이션했다. 청크 간격 변경은 새로 생성되는 청크부터 적용되므로, 기존 데이터는 새 Hypertable로 이동해야 했다.
```sql
-- 청크 간격 변경 (새 청크부터 적용)
SELECT set_chunk_time_interval('sensor_data', INTERVAL '1 hour');
```### ケース 2: 圧縮 segmentby の未設定によるクエリのパフォーマンスの悪化
圧縮を有効にしながら`compress_segmentby`を指定しなかった。すべてのデータが単一セグメントに圧縮され、`WHERE sensor_id = 42`同じフィルタクエリがチャンク全体を解放する必要がありました。圧縮前よりクエリが3倍遅くなった。
**回復**:すべての圧縮チャンクを解放し、`compress_segmentby`を設定した後、再圧縮した。大規模なデータでは、この処理に数時間かかることがあるため、最初から正しく設定することが重要です。
###ケース3:保存ポリシーなしで運用してストレージを枯渇
3年間保存ポリシーなしでIoTセンサーデータを積載した。全体のデータサイズが15TBに達し、分析クエリはほとんど最近30日のデータのみを使用した。ストレージコストは月数千ドルに達した。
**回復**: 連続集計を生成して過去データの時間別/日別集計を先に確保した後、元データに90日保存ポリシーを適用した。ストレージが15TBから800GBに減少しました。
## 15. 本番チェックリスト
### Hypertableデザイン
- []チャンク間隔が1日のデータ量とメモリに合わせて設定されているか
- [ ]多次元パーティショニングが必要かどうか検討したか(固有デバイス/センサ数が非常に多い場合)
- [ ] 時間列のタイムゾーン処理方式が統一されたか(TIMESTAMPTZ推奨)
###インデックス
- [ ] 主要クエリパターンに適合する複合インデックスが存在するか
- [ ]不要なインデックスがINSERT性能を低下させていないか
- [ ] 部分インデックスを活用してインデックスサイズを最適化したか
###圧縮
- [ ]`compress_segmentby`がクエリパターンと一致するか
- [ ]圧縮ポリシーの時間オフセットが遅く到着するデータを考慮しましたか?
- [ ]圧縮率が期待レベル(80%以上)を達成しているか
### 連続集計
- [ ]ダッシュボード/レポートクエリに連続集計が活用されているか
- [ ] 更新周期とオフセットがデータ遅延特性に合わせて設定されているか
- [ ] 階層集計(時間別 -> 日別)が構成されたか
### データ保存
- [ ] 元データの保存期間が設定されているか
- [ ] 集計データの保存期間が元より長く設定されているか
- [ ] 保存ポリシージョブが正常に実行されているか(job_stats確認)
###モニタリング
- [ ] ディスク使用量の監視と通知が設定されているか
- [ ]バックグラウンドジョブ(圧縮、保存、集計)実行状態を監視するか
- []スロークエリロギングが有効になっているか(`log_min_duration_statement`)
- []チャンク数が過度に増加していないか
### バックアップとリカバリ
- [ ]`pg_dump`または`pg_basebackup`で定期バックアップが設定されているか
- [ ]回復テストを定期的に行ったか
- [ ] WALアーカイブでポイントインタイム回復が可能か
## 16. 参考資料
- [TimescaleDB公式ドキュメント](https://docs.timescale.com/)
- [TimescaleDB GitHubリポジトリ](https://github.com/timescale/timescaledb)
- [連続集計(Continuous Aggregates)公式ガイド](https://docs.timescale.com/use-timescale/latest/continuous-aggregates/)
- [ネイティブ圧縮公式ガイド](https://docs.timescale.com/use-timescale/latest/compression/)
- [Timescale公式ブログ](https://blog.timescale.com/)
- [TimescaleDB vs InfluxDB - 公式比較](https://docs.timescale.com/about/latest/timescaledb-vs-influxdb/)
- [TimescaleDB Hyperfunctions APIリファレンス](https://docs.timescale.com/api/latest/hyperfunctions/)