Pourquoi une base de séries temporelles
Une série temporelle est une suite de valeurs datées : la température d'une sonde toutes les dix secondes, le nombre de commandes par minute. On l'écrit beaucoup, on la modifie rarement, et on l'interroge presque toujours par intervalle de temps.
QuestDB est construit autour de cet usage. Chaque table a une colonne de temps désignée et se découpe en partitions (par jour ou par heure), ce qui garde rapides les requêtes sur une période et rend la suppression des anciennes données immédiate. Son SQL reste du SQL, enrichi de quelques mots-clés propres au temps.
Ce que QuestDB apporte
- SQL des séries temporelles —
SAMPLE BY,LATEST ONetASOF JOINen plus du SQL standard. - Ingestion continue — InfluxDB Line Protocol sur HTTP, avec des clients officiels pour Python, Go, Java, Node.js, Rust et .NET.
- Console web — éditeur SQL, import de CSV par glisser-déposer, graphiques des résultats.
- API REST — requêtes, import et export CSV en HTTP.
- Protocole PostgreSQL — vos pilotes et outils PostgreSQL habituels.
- Partitions de temps — requêtes rapides sur une période, suppression des anciennes données en une commande.
QuestDB face à une table PostgreSQL classique
Faites défiler le tableau
| QuestDB | PostgreSQL seul | |
|---|---|---|
| Conçu pour | Mesures horodatées en continu | Données relationnelles générales |
| Agréger par intervalle | SAMPLE BY 1h | date_trunc et GROUP BY |
| Dernière valeur par série | LATEST ON | Sous-requête ou DISTINCT ON |
| Aligner deux flux dans le temps | ASOF JOIN | Jointure latérale à écrire |
| Supprimer l'historique ancien | DROP PARTITION | DELETE puis VACUUM |
| Ingestion | Line Protocol, CSV, SQL | SQL et COPY |
Prérequis
Un VPS Ubuntu 24.04 avec 2 Go de RAM au minimum et 20 Go de disque, Docker et le plugin Compose installés, et un nom (sous-domaine) qui pointe vers le VPS pour le HTTPS. QuestDB tourne sur la JVM : prévoyez 4 Go de RAM si vous ingérez beaucoup de données ou lancez de grosses requêtes.
Installer QuestDB avec Docker Compose
Préparer le répertoire et le mot de passe
Créez le répertoire de l'application et un fichier
.envqui porte le mot de passe de l'utilisateuradmin.mkdir -p /opt/questdb && cd /opt/questdb echo "ADMIN_PASSWORD=$(openssl rand -hex 16)" > .env chmod 600 .envÉcrire le docker-compose.yml
Les ports ne sont publiés que sur la boucle locale : seul nginx atteint la console et l'API.
QDB_HTTP_USERetQDB_HTTP_PASSWORDprotègent toute l'API HTTP,QDB_PG_PASSWORDremplace le mot de passe d'usine du protocole PostgreSQL. L'ingestion sur TCP, sans authentification en version open source, est coupée, etQDB_TELEMETRY_ENABLEDdésactive la télémétrie anonyme.services: questdb: image: questdb/questdb:10.1.0 restart: unless-stopped ports: - "127.0.0.1:9000:9000" - "127.0.0.1:8812:8812" environment: QDB_HTTP_USER: "admin" QDB_HTTP_PASSWORD: "${ADMIN_PASSWORD}" QDB_PG_USER: "admin" QDB_PG_PASSWORD: "${ADMIN_PASSWORD}" QDB_LINE_TCP_ENABLED: "false" QDB_HTTP_SETTINGS_READONLY: "true" QDB_TELEMETRY_ENABLED: "false" volumes: - questdb-data:/var/lib/questdb volumes: questdb-data: {}Démarrer et vérifier
Le démarrage prend quelques secondes. Sans identifiants, l'API répond 401 ; avec, elle renvoie la version installée.
docker compose up -d curl -s -o /dev/null -w '%{http_code}\n' 'http://127.0.0.1:9000/exec?query=SELECT%201' curl -u admin:VOTRE_MOT_DE_PASSE 'http://127.0.0.1:9000/exec?query=SELECT%20build()'Publier en HTTPS avec nginx
Dans le vhost de votre domaine (certificat obtenu par certbot), proxifiez vers le port local. Relevez
client_max_body_sizepour les imports CSV.location / { proxy_pass http://127.0.0.1:9000; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; client_max_body_size 256m; }
Sans QDB_HTTP_USER, l'API est ouverte à tous
Sans utilisateur déclaré, QuestDB accepte toute requête HTTP : quiconque connaît l'adresse peut lire vos données et créer, modifier ou supprimer des tables en SQL. Gardez l'authentification active, changez le mot de passe d'usine du protocole PostgreSQL, et ne publiez jamais les ports 9000, 8812 ou 9009 directement sur l'IP publique : passez toujours par le reverse proxy HTTPS.
Envoyer des données
Le plus simple est l'InfluxDB Line Protocol sur HTTP : une ligne par mesure, avec le nom de la table, des étiquettes et des valeurs. La table et ses colonnes sont créées à la première écriture.
curl -u admin:VOTRE_MOT_DE_PASSE -X POST \
--data-binary 'capteurs,sonde=s1 temperature=21.5' \
https://votre-domaine/writeDepuis votre code, un client officiel regroupe les lignes et gère les reprises. En Python, après pip install questdb :
from questdb.ingress import Sender, TimestampNanos
conf = "https::addr=votre-domaine:443;username=admin;password=VOTRE_MOT_DE_PASSE;"
with Sender.from_conf(conf) as sender:
sender.row(
"capteurs",
symbols={"sonde": "s1"},
columns={"temperature": 21.5},
at=TimestampNanos.now(),
)Interroger vos séries en SQL
Dans la console web ou par l'API REST (/exec?query=…), le SQL standard fonctionne, et les extensions de temps évitent les requêtes compliquées.
-- Moyenne horaire par sonde
SELECT timestamp, sonde, avg(temperature)
FROM capteurs
SAMPLE BY 1h;
-- Dernière valeur connue de chaque sonde
SELECT * FROM capteurs LATEST ON timestamp PARTITION BY sonde;Un programme qui tourne sur le VPS lui-même peut aussi se connecter par le protocole PostgreSQL sur 127.0.0.1:8812, base qdb, utilisateur admin et le même mot de passe.
Bien modéliser une table de séries temporelles
La création automatique à la première écriture suffit pour démarrer. Pour une table appelée à durer, déclarez-la vous-même : vous choisissez les types, la colonne de temps et le découpage.
CREATE TABLE capteurs (
timestamp TIMESTAMP,
sonde SYMBOL,
temperature DOUBLE
) TIMESTAMP(timestamp) PARTITION BY DAY WAL
DEDUP UPSERT KEYS(timestamp, sonde);Trois choix comptent. La colonne de temps désignée (TIMESTAMP(...)) est celle sur laquelle QuestDB trie et partitionne : toutes les extensions de temps s'appuient sur elle. Le type SYMBOL convient aux étiquettes répétées (nom de sonde, code d'appareil, symbole boursier) : la valeur est stockée une fois et référencée, ce qui économise le disque et accélère les filtres. Le partitionnement (PARTITION BY DAY ou HOUR) se choisit selon le volume : une partition doit rester d'une taille raisonnable, et c'est l'unité que vous supprimerez plus tard.
La clause DEDUP UPSERT KEYS rend l'ingestion idempotente : si un client renvoie la même mesure après une coupure réseau, la ligne est remplacée au lieu d'être dupliquée.
Importer et exporter des fichiers CSV
La console accepte les CSV par glisser-déposer. En ligne de commande, /imp importe un fichier dans une table et /exp exporte le résultat d'une requête.
curl -u admin:VOTRE_MOT_DE_PASSE -F [email protected] 'https://votre-domaine/imp?name=mesures'
curl -u admin:VOTRE_MOT_DE_PASSE -G --data-urlencode 'query=SELECT * FROM mesures' https://votre-domaine/exp > export.csvRétention, disque et sauvegarde
Rien n'expire tout seul. Supprimez les partitions anciennes en SQL et planifiez la commande si besoin. Pour une sauvegarde cohérente, figez l'état de la base le temps de copier le volume questdb-data.
ALTER TABLE capteurs DROP PARTITION
WHERE timestamp < dateadd('d', -90, now());
CHECKPOINT CREATE;
-- copiez le volume questdb-data, puis :
CHECKPOINT RELEASE;Mettre à jour QuestDB
Lisez les notes de version avant de changer de version majeure, puis remplacez le tag de l'image et redémarrez : les données du volume sont conservées.
cd /opt/questdb
# changez le tag de l'image dans docker-compose.yml, puis :
docker compose pull && docker compose up -dErreurs fréquentes et dépannage
401 sur chaque requête. L'utilisateur ou le mot de passe ne correspond pas à QDB_HTTP_USER et QDB_HTTP_PASSWORD. Vérifiez le contenu du fichier .env, puis redémarrez le conteneur après toute modification : les variables ne sont lues qu'au démarrage.
413 lors d'un import CSV. Le fichier dépasse la limite de nginx. Relevez client_max_body_size dans le vhost, rechargez nginx, ou découpez le fichier.
Le conteneur redémarre en boucle. Lisez docker compose logs questdb. La cause la plus fréquente est la mémoire : QuestDB tourne sur la JVM et un VPS de 1 Go ne suffit pas. Passez à 2 Go au minimum.
Le disque se remplit. Rien n'expire par défaut : planifiez la suppression des anciennes partitions et surveillez la taille du volume questdb-data avec docker system df -v.
Une application du VPS n'arrive pas à se connecter en PostgreSQL. Utilisez 127.0.0.1:8812, la base qdb, l'utilisateur admin et le mot de passe du .env : le mot de passe d'usine quest est refusé.
Installation en un clic depuis le Marketplace ServOrbit
Sur un VPS ServOrbit, QuestDB s'installe depuis le Marketplace : le script déploie le conteneur officiel, génère le mot de passe de l'utilisateur admin, active l'authentification sur l'API HTTP et le protocole PostgreSQL, coupe la télémétrie et configure nginx en HTTPS, sur un sous-domaine de votre domaine ou sur le sous-domaine gratuit du VPS. Le mot de passe s'affiche dans votre espace client.