Skip to content
Learni
View all tutorials
Bases de données

Comment maîtriser TimescaleDB en 2026

Introduction

TimescaleDB transforme PostgreSQL en une base de données optimisée pour les séries temporelles, gérant des milliards de points de données IoT, monitoring ou finance sans sacrifier les performances SQL standard. Contrairement à InfluxDB ou Prometheus, elle conserve la compatibilité PostgreSQL totale : jointures complexes, transactions ACID et extensions comme PostGIS. En 2026, avec l'essor de l'edge computing, maîtriser les hypertables (tables partitionnées automatiquement par temps), la compression (jusqu'à 90% d'économie), les agrégats continus (materialized views auto-raffraîchies) et les hyperfunctions (requêtes vectorielles UDF) est essentiel pour les architectes de données.

Ce tutoriel expert vous guide pas à pas : de l'installation Docker à l'optimisation production, avec code SQL copier-collable. Imaginez stocker 1 To de métriques par jour compressées en 100 Go, interrogées en <100ms. À la fin, vous bookmarkederez ce guide pour vos projets critiques.

Prérequis

  • PostgreSQL 14+ (ou Docker pour simplicité)
  • Client psql installé
  • Connaissances avancées en SQL et partitioning PostgreSQL
  • 4 Go RAM minimum pour tests (16+ recommandés)
  • Outils : Docker et pgAdmin optionnel pour visualiser

Installation via Docker Compose

docker-compose.yml
version: '3.8'
services:
  timescale:
    image: timescale/timescaledb:latest-pg16
    container_name: timescaledb
    restart: always
    environment:
      POSTGRES_PASSWORD: mysecretpassword
      POSTGRES_USER: postgres
      POSTGRES_DB: timescaledb_tutorial
    ports:
      - "5432:5432"
    volumes:
      - timescale_data:/var/lib/postgresql/data
volumes:
  timescale_data:
    driver: local

Ce docker-compose.yml déploie TimescaleDB 2.15+ sur PostgreSQL 16 avec persistence via volume. Lancez avec docker compose up -d. L'image officielle inclut l'extension activée par défaut, évitant les pièges d'installation manuelle comme les dépendances shared_preload_libraries. Vérifiez avec docker logs timescaledb.

Connexion et activation de l'extension

Connectez-vous via psql -h localhost -U postgres -d timescaledb_tutorial (mot de passe: mysecretpassword). L'extension timescaledb est pré-chargée, mais activez-la par base avec CREATE EXTENSION. Testez avec SELECT extname FROM pg_extension WHERE extname = 'timescaledb';.

Création d'une hypertable basique

create_hypertable.sql
CREATE TABLE metrics (
  time TIMESTAMPTZ NOT NULL,
  device_id TEXT NOT NULL,
  temperature DOUBLE PRECISION,
  humidity DOUBLE PRECISION,
  location POINT
);

SELECT create_hypertable('metrics', 'time', chunk_time_interval => INTERVAL '1 day', partitioning_column => 'device_id', number_partitions => 4);

-- Insertion de données de test
INSERT INTO metrics (time, device_id, temperature, humidity, location)
SELECT
  NOW() - INTERVAL '1 day' * generate_series(0, 99),
  'device_' || (random() * 10)::INT,
  20 + random() * 10,
  40 + random() * 20,
  POINT(random() * 100, random() * 100)
FROM generate_series(1, 10000);

Cette hypertable partitionne par time (chunks quotidiens) et device_id (4 partitions hash). Les 10k insertions simulent des capteurs IoT. Piège : sans chunk_time_interval, défaut 7 jours trop large pour high-velocity data ; ajustez à 1h pour <1M rows/chunk.

Requêtes optimisées sur hypertables

Les hypertables appliquent automatiquement constraint exclusion : seules les chunks pertinentes sont scannées. Utilisez time_bucket pour bucketing et last() pour latest values, comme dans Grafana.

Requêtes avancées avec time_bucket

queries.sql
-- Moyenne horaire par device
SELECT
  time_bucket('1 hour', time) AS bucket,
  device_id,
  AVG(temperature) AS avg_temp,
  LAST(humidity, time) AS last_humidity,
  COUNT(*) AS points
FROM metrics
WHERE time > NOW() - INTERVAL '2 days'
GROUP BY 1, 2
ORDER BY 1 DESC, 2;

-- Top 5 devices les plus chauds
SELECT
  device_id,
  MAX(temperature) AS max_temp,
  ST_X(location) AS lon,
  ST_Y(location) AS lat
FROM metrics
WHERE time > NOW() - INTERVAL '1 day'
GROUP BY device_id, location
ORDER BY max_temp DESC
LIMIT 5;

time_bucket downsample en buckets ; LAST optimise vs window functions. PostGIS POINT permet géo-queries. Résultat : <10ms sur 1M rows grâce à index chunk-level. Évitez NOW() en prod, utilisez params pour query planner.

Agrégats continus (Continuous Aggregates)

continuous_agg.sql
-- Vue pour hourly aggregates
CREATE MATERIALIZED VIEW hourly_metrics
WITH (timescaledb.continuous) AS
SELECT
  time_bucket('1 hour', time) AS bucket,
  device_id,
  AVG(temperature) AS avg_temp,
  AVG(humidity) AS avg_hum,
  MIN(temperature) AS min_temp,
  MAX(temperature) AS max_temp
FROM metrics
GROUP BY 1, 2
WITH DATA;

-- Politiques de rafraîchissement
SELECT add_continuous_aggregate_policy('hourly_metrics',
  start_offset => INTERVAL '2 days',
  end_offset => INTERVAL '1 hour',
  schedule_interval => INTERVAL '1 hour');

-- Query sur la vue (toujours fraîche)
SELECT * FROM hourly_metrics WHERE bucket > NOW() - INTERVAL '1 day';

Les continuous aggs auto-refreshent les materialized views, idéales pour dashboards. add_continuous_aggregate_policy planifie jobs background. Gain : queries 100x plus rapides sur historical data. Piège : end_offset doit matcher bucket pour éviter real-time lag.

Compression des chunks

Activez la compression pour réduire stockage de 90% via Gorilla + Delta-of-Delta. Seuls chunks >7 jours sont éligibles par défaut.

Activation et compression manuelle

compression.sql
-- Activer compression sur hypertable
ALTER TABLE metrics SET (
  timescaledb.compress,
  timescaledb.compress_segmentby = 'device_id',
  timescaledb.compress_orderby = 'time DESC'
);

-- Compressor chunk le plus ancien
SELECT compress_chunk(i.chunk_schema || '.' || i.chunk_name)
FROM timescaledb_information.chunks i
WHERE i.hypertable_name = 'metrics'
ORDER BY i.range_start DESC
LIMIT 1;

-- Decompresser pour writes (si needed)
SELECT decompress_chunk(c)
FROM show_chunks('metrics', older_than => INTERVAL '3 days') c;

-- Stats compression
SELECT
  hypertable_name,
  sg.num_rows_pre_compression,
  sg.num_rows_post_compression,
  pg_size_pretty(sg.size_pre_compression) AS size_pre,
  pg_size_pretty(sg.size_post_compression) AS size_post
FROM timescaledb_information.compressed_chunk_stats sg
JOIN timescaledb_information.hypertables h ON h.hypertable_name = sg.hypertable_name
WHERE h.hypertable_name = 'metrics';

compress_orderby optimise décompression partielle. segmentby grouppe par device pour locality. Résultat : 10x réduction taille. Attention : compression irréversible sur reads sans decompress ; testez sur staging.

Politiques de rétention

retention.sql
-- Drop automatique chunks > 30 jours
SELECT add_retention_policy('metrics',
  INTERVAL '30 days');

-- Custom : drop compressed only
SELECT add_retention_policy('metrics',
  INTERVAL '90 days',
  if_exists => 'ignore');

-- Lister jobs
SELECT job_id, proc_name, last_run_status
FROM timescaledb_information.jobs
WHERE proc_name LIKE '%retention%';

-- Pause job
SELECT alter_job(job_id, scheduled => false)
FROM timescaledb_information.jobs
WHERE hypertable_name = 'metrics';

Les retention policies sont des jobs BG qui droppent chunks entiers, libérant disque auto. if_exists => 'ignore' évite erreurs. Idéal pour compliance GDPR. Piège : sur compressed hypertables, retient espace jusqu'à vacuum post-drop.

Hyperfunctions avancées

En 2026, les hyperfunctions comme lttb() (Largest Triangle Three Buckets) downsamplent pour Grafana sans perte visuelle.

Exemple hyperfunction LTTB

hyperfunctions.sql
-- Downsampling pour graphs (100 points max)
SELECT
  time_bucket('5 minutes', time) AS bucket,
  device_id,
  lttb(temperature, 20, 'time') OVER (PARTITION BY device_id ORDER BY time)
FROM metrics
WHERE time > NOW() - INTERVAL '1 hour'
GROUP BY 1, 2
ORDER BY 1, 2;

-- Gap filling
SELECT
  time_bucket('1 hour', generate_series(NOW() - INTERVAL '24 hours', NOW(), '1 hour')) AS bucket,
  device_id,
  COALESCE(AVG(temperature), 0) AS interp_temp,
  interpolate(gaps(temperature)) AS filled_temp
FROM metrics
GROUP BY 1, 2
ORDER BY 1, 2;

lttb réduit points tout en préservant forme courbe ; gaps/interpolate comble trous. Parfait pour alerting. Nécessite extension activée. Évitez sur uncompressed récents pour perf.

Bonnes pratiques

  • Chunk sizing : Visez 25-50M rows/chunk avec chunk_time_interval basé sur ingestion rate (ex: 1h pour 1k/sec).
  • Indexation : Ajoutez BRIN sur time + GIN sur tags JSONB post-hypertable.
  • Monitoring : Query timescaledb_information.hypertable et chunks quotidiennement ; alertez si >100 chunks actifs.
  • Scaling : Utilisez distributed hypertables pour multi-nodes via create_distributed_hypertable.
  • Backup : pg_dumpall + WAL pour chunks ; WAL-E pour S3.

Erreurs courantes à éviter

  • Pas d'ORDER BY dans compression : Dégrade décompression séquentielle, +50% CPU.
  • Chunks trop petits : Overhead metadata ; surveillez via timescaledb_information.chunks.
  • Oubli end_offset dans cont. aggs : Lag real-time jusqu'à bucket size.
  • Writes sur compressed chunks : Force decompress ; segmentez par TTL.

Pour aller plus loin