In This Guide
Time-series data is the fastest-growing data category. IoT sensors, application metrics, financial ticks, server logs — all timestamped, append-mostly, and queried by time range. General-purpose databases handle this poorly at scale because they weren't designed for the access patterns: massive writes, time-range reads, downsampling, and automatic data expiry.
1. What Makes Time-Series Data Different
| Characteristic | General OLTP | Time-Series |
|---|---|---|
| Write pattern | Random inserts + updates | Append-only, sequential timestamps |
| Read pattern | Point lookups by ID | Time-range scans with aggregations |
| Updates | Frequent | Rare (immutable after write) |
| Data lifecycle | Keep forever | Downsample + expire (hot → warm → cold) |
| Write volume | Hundreds to thousands/sec | Hundreds of thousands to millions/sec |
| Compression | General-purpose | Time-aware (delta-of-delta, gorilla) |
2. Database Comparison
| Feature | InfluxDB 3.0 | TimescaleDB | QuestDB |
|---|---|---|---|
| Query language | SQL + InfluxQL | Full SQL (PostgreSQL) | SQL (PostgreSQL wire) |
| Write speed | Very fast (columnar) | Fast (PostgreSQL-based) | Fastest (memory-mapped) |
| JOINs | Limited | Full (it's PostgreSQL) | ASOF JOIN (time-series specific) |
| Downsampling | Built-in tasks | Continuous aggregates | SAMPLE BY clause |
| Ecosystem | Telegraf, Grafana | Full PostgreSQL ecosystem | Grafana, PostgreSQL wire |
| Best for | Monitoring, IoT | Mixed workloads (TS + relational) | High-frequency ingestion, fintech |
3. InfluxDB — The Time-Series Specialist
Writing Sensor Data (Line Protocol)
# InfluxDB Line Protocol: measurement,tags fields timestamp
temperature,sensor=sensor-001,location=mumbai value=28.5 1706918400000000000
temperature,sensor=sensor-002,location=delhi value=22.1 1706918400000000000
humidity,sensor=sensor-001,location=mumbai value=65.2 1706918400000000000
# Python client
from influxdb_client import InfluxDBClient, Point
from influxdb_client.client.write_api import SYNCHRONOUS
client = InfluxDBClient(url="http://localhost:8086", token="my-token", org="my-org")
write_api = client.write_api(write_options=SYNCHRONOUS)
# Write individual points
point = Point("temperature") \
.tag("sensor", "sensor-001") \
.tag("location", "mumbai") \
.field("value", 28.5) \
.time(datetime.utcnow())
write_api.write(bucket="iot-data", record=point)
# Batch write (much faster — always batch in production)
points = [
Point("temperature").tag("sensor", f"sensor-{i:03d}").field("value", reading)
for i, reading in enumerate(sensor_readings)
]
write_api.write(bucket="iot-data", record=points)
Querying — SQL and Flux
-- InfluxDB 3.0 SQL: Average temperature per hour, last 24 hours
SELECT
date_bin('1 hour', time) AS hour,
sensor,
AVG(value) AS avg_temp,
MAX(value) AS max_temp,
MIN(value) AS min_temp
FROM temperature
WHERE time > now() - INTERVAL '24 hours'
AND location = 'mumbai'
GROUP BY hour, sensor
ORDER BY hour DESC;
4. TimescaleDB — PostgreSQL for Time-Series
TimescaleDB is a PostgreSQL extension — you get full SQL, JOINs, and the entire PostgreSQL ecosystem, with time-series optimizations bolted on. If your team knows PostgreSQL, this is the fastest path to production.
Setup and Schema
-- Create a regular PostgreSQL table
CREATE TABLE sensor_data (
time TIMESTAMPTZ NOT NULL,
sensor_id TEXT NOT NULL,
location TEXT,
temperature DOUBLE PRECISION,
humidity DOUBLE PRECISION,
battery DOUBLE PRECISION
);
-- Convert to hypertable (automatic time-based partitioning)
SELECT create_hypertable('sensor_data', by_range('time'));
-- Compression policy (10x storage reduction)
ALTER TABLE sensor_data SET (
timescaledb.compress,
timescaledb.compress_segmentby = 'sensor_id',
timescaledb.compress_orderby = 'time DESC'
);
SELECT add_compression_policy('sensor_data', INTERVAL '7 days');
-- Retention policy (auto-delete old data)
SELECT add_retention_policy('sensor_data', INTERVAL '90 days');
-- Continuous aggregate (pre-computed hourly rollups)
CREATE MATERIALIZED VIEW sensor_hourly
WITH (timescaledb.continuous) AS
SELECT
time_bucket('1 hour', time) AS hour,
sensor_id,
AVG(temperature) AS avg_temp,
MAX(temperature) AS max_temp,
MIN(temperature) AS min_temp,
COUNT(*) AS readings
FROM sensor_data
GROUP BY hour, sensor_id;
-- Auto-refresh the continuous aggregate
SELECT add_continuous_aggregate_policy('sensor_hourly',
start_offset => INTERVAL '3 hours',
end_offset => INTERVAL '1 hour',
schedule_interval => INTERVAL '1 hour');
5. QuestDB — Speed-First Design
QuestDB is designed for maximum ingestion speed — memory-mapped files, column-oriented storage, and SIMD-accelerated queries. It's the fastest time-series database for write-heavy workloads.
-- Create partitioned table
CREATE TABLE sensor_data (
timestamp TIMESTAMP,
sensor_id SYMBOL, -- SYMBOL type: dictionary-encoded for fast grouping
location SYMBOL,
temperature DOUBLE,
humidity DOUBLE
) timestamp(timestamp) PARTITION BY DAY WAL;
-- SAMPLE BY: time-series specific aggregation
SELECT
timestamp,
sensor_id,
avg(temperature) AS avg_temp,
max(temperature) AS max_temp,
count() AS readings
FROM sensor_data
WHERE timestamp IN '2026-02'
SAMPLE BY 1h
FILL(LINEAR); -- Interpolate missing data points
-- ASOF JOIN: join two time-series by closest timestamp
SELECT t.timestamp, t.temperature, h.humidity
FROM temperature_readings t
ASOF JOIN humidity_readings h ON (t.sensor_id = h.sensor_id);
-- Matches each temperature reading with the closest-in-time humidity reading
-- LATEST ON: get most recent value per sensor (common IoT query)
SELECT * FROM sensor_data
LATEST ON timestamp PARTITION BY sensor_id;
6. IoT Data Architecture Patterns
IoT Sensors (1000s of devices)
│
├── MQTT Broker (Mosquitto / HiveMQ)
│ │
│ ▼
│ Message Router (Kafka / NATS)
│ │
│ ├──→ Time-Series DB (raw data, 30-day retention)
│ │ │
│ │ ├──→ Continuous aggregates (hourly rollups, 1-year retention)
│ │ └──→ Grafana dashboards (real-time monitoring)
│ │
│ ├──→ Rules Engine (threshold alerts)
│ │ └──→ Alerting (PagerDuty, Telegram)
│ │
│ └──→ Cold Storage (S3 / data lake, archive)
│
└── Edge Processing (filter noise, aggregate at device)
| Use Case | Best TSDB | Why |
|---|---|---|
| Infrastructure monitoring (Prometheus stack) | Prometheus + Thanos/Mimir | Pull-based, built for Kubernetes |
| IoT sensors + need SQL JOINs | TimescaleDB | Full PostgreSQL with time-series features |
| High-frequency trading / fintech | QuestDB | Fastest ingestion, ASOF JOIN for tick data |
| General IoT + existing Telegraf/Grafana | InfluxDB | Best agent ecosystem, built-in alerting |
| Already using PostgreSQL, moderate scale | TimescaleDB | Drop-in extension, no new infrastructure |
Frequently Asked Questions
Can't I just use PostgreSQL for time-series data?
Yes, up to a point. PostgreSQL handles time-series data well at moderate scale (thousands of writes/second, under 100GB). Beyond that, you need automatic partitioning, compression, and downsampling — which is exactly what TimescaleDB adds to PostgreSQL. For massive scale, purpose-built TSDBs like InfluxDB or QuestDB outperform by 10-100x.
How do I handle downsampling?
Keep raw data for 7-30 days, hourly aggregates for 1-2 years, daily aggregates forever. All three TSDBs support this: TimescaleDB with continuous aggregates, InfluxDB with tasks, QuestDB with materialized views. Automate it — manual downsampling is a maintenance burden that teams always neglect.
InfluxDB 2.x or 3.0?
InfluxDB 3.0 is a complete rewrite — Apache Arrow-based, columnar storage, SQL support. It's significantly faster than 2.x. New projects should use 3.0. Existing 2.x deployments can continue — the Flux query language still works but is being replaced by SQL. The migration path is straightforward.
How do I visualize time-series data?
Grafana is the standard — it connects to all three TSDBs natively. For embedded dashboards in your app, consider Apache ECharts or Plotly. For alerting, Grafana Alerting or the TSDB's built-in alerting (InfluxDB has this; TimescaleDB uses PostgreSQL triggers or external tools).
What about Prometheus?
Prometheus is great for infrastructure metrics (CPU, memory, request latency) in Kubernetes environments. It's pull-based (scrapes targets) vs push-based (like InfluxDB). For IoT and application telemetry, InfluxDB or TimescaleDB are better fits. For infrastructure monitoring, Prometheus + Grafana is the standard. You can use both: Prometheus for infra, a TSDB for application data.
Pillai Infotech LLP
We build IoT data platforms and monitoring systems — from sensor ingestion to real-time dashboards. Let's architect your time-series infrastructure.