Time-Series Database Low-Level Design

What is a Time-Series Database?

A time-series database (TSDB) is optimized for storing and querying data points indexed by time: metrics (CPU usage, request rate), IoT sensor readings, financial tick data, and application traces. Time-series data has distinct characteristics: high write volume (thousands of data points per second), write-once (data is never updated), queries are almost always range-based (last 1 hour, last 7 days), and recent data is accessed far more often than old data. InfluxDB, TimescaleDB, Prometheus, and Druid are purpose-built TSDBs.

Requirements

  • Ingest 500K data points/second (metrics from 10K servers, 50 metrics each)
  • Point query: get CPU usage for server-42 at 2026-04-12 14:00:00
  • Range query: average CPU across all servers over the last 1 hour, grouped by 5-minute windows
  • Downsampling: automatically roll up high-resolution data to lower resolution as it ages
  • Data retention: keep raw data for 7 days, hourly aggregates for 1 year, daily aggregates forever
  • Query latency: <100ms for queries spanning up to 7 days of raw data

Core Data Structure

Measurement: the "table" — e.g., "cpu_usage", "request_latency", "temperature"

Tags: indexed metadata — {"host": "server-42", "region": "us-east-1", "env": "prod"}
      Tags are used in WHERE clauses for filtering

Fields: actual values — {"value": 73.5}
        Fields are not indexed (too high cardinality); only values to query

Timestamp: nanosecond precision Unix timestamp

Data point example:
  measurement="cpu_usage"
  tags={host="server-42", region="us-east-1"}
  fields={value=73.5}
  time=1712935200000000000

Storage Design

Time-series data is stored in time-ordered chunks. Within each chunk, data is column-oriented and compressed:

-- TimescaleDB (PostgreSQL extension) approach:
CREATE TABLE cpu_usage (
    time        TIMESTAMPTZ NOT NULL,
    host        TEXT,
    region      TEXT,
    value       DOUBLE PRECISION
);

-- Convert to hypertable: auto-partitions by time
SELECT create_hypertable('cpu_usage', 'time', chunk_time_interval => INTERVAL '1 day');

-- Each partition (chunk) is a 1-day range of data
-- TimescaleDB auto-creates and manages chunks
-- Compression: SELECT add_compression_policy('cpu_usage', INTERVAL '7 days');
-- Old chunks compressed to ~10% of raw size

-- Index for tag-based filtering
CREATE INDEX ON cpu_usage (host, time DESC);

Write Path: Batching and LSM

For 500K points/second, individual inserts are too slow. Write path:

  1. Ingest API receives metrics in batches (e.g., 1000 points per POST)
  2. Points written to an in-memory write buffer (WAL for durability)
  3. Buffer flushed to disk as immutable sorted files (SSTable) every few seconds
  4. Background compaction merges small files into larger ones

This is Log-Structured Merge (LSM) — the same approach used by Cassandra, LevelDB, and RocksDB. Writes are always sequential (no random I/O), which is much faster than B-tree updates.

Downsampling and Retention

-- TimescaleDB continuous aggregates (auto-updating materialized views)
CREATE MATERIALIZED VIEW cpu_hourly
WITH (timescaledb.continuous) AS
SELECT
    time_bucket('1 hour', time) AS bucket,
    host, region,
    avg(value) AS avg_cpu,
    max(value) AS max_cpu,
    min(value) AS min_cpu
FROM cpu_usage
GROUP BY bucket, host, region;

-- Refresh policy: update as new data arrives
SELECT add_continuous_aggregate_policy('cpu_hourly',
    start_offset => INTERVAL '3 hours',
    end_offset   => INTERVAL '1 hour',
    schedule_interval => INTERVAL '1 hour');

-- Retention policies:
SELECT add_retention_policy('cpu_usage', INTERVAL '7 days');     -- raw: 7 days
SELECT add_retention_policy('cpu_hourly', INTERVAL '1 year');    -- hourly: 1 year

Query Optimization

-- Range query with time bucketing
SELECT time_bucket('5 minutes', time) AS bucket,
       avg(value) as avg_cpu
FROM cpu_usage
WHERE time >= NOW() - INTERVAL '1 hour'
  AND host = 'server-42'
GROUP BY bucket
ORDER BY bucket;

-- Execution plan: index scan on (host, time DESC) → only scans 1-hour chunk
-- Without time partitioning, this would scan all historical data

Key Design Decisions

  • Time-based partitioning — queries touch only the relevant time chunks, not the full dataset
  • LSM writes — sequential writes, no random I/O; handles 500K points/second on commodity hardware
  • Continuous aggregates for downsampling — pre-computed rollups, queries on hourly/daily data are instant
  • Tags indexed, fields not — tags have bounded cardinality (100 hosts), fields can be floats at every second
  • Column-oriented compressed chunks — 10-100× better compression than row storage for numeric time-series data

{“@context”:”https://schema.org”,”@type”:”FAQPage”,”mainEntity”:[{“@type”:”Question”,”name”:”What makes a time-series database different from a regular relational database?”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”Time-series databases are optimized for insert-heavy, append-only workloads with time-based range queries. Key differences: (1) Time-based partitioning — data is automatically split into chunks by time, so queries only scan relevant periods. (2) Column-oriented storage with compression — numeric time-series data compresses 10-100x better than row storage. (3) LSM tree writes — sequential appends, no random I/O. (4) Automatic downsampling and retention — data ages from raw to hourly to daily aggregates automatically.”}},{“@type”:”Question”,”name”:”How does TimescaleDB handle 500K data points per second?”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”TimescaleDB (a PostgreSQL extension) uses hypertables that automatically partition data into time-based chunks. Writes go to the current chunk — a standard PostgreSQL table with a time range. TimescaleDB’s compression converts old chunks from row format to columnar with delta-delta encoding for timestamps and XOR compression for floats, achieving 90%+ compression. Batch inserts (COPY or multi-row INSERT) are essential — individual row inserts at 500K/s would overwhelm even TimescaleDB.”}},{“@type”:”Question”,”name”:”What is downsampling in time-series databases and why do you need it?”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”Downsampling aggregates high-resolution data into lower-resolution summaries as it ages. Raw data (one point per second) is kept for 7 days; hourly averages for 1 year; daily averages forever. Without downsampling, 1-year queries over raw data would scan trillions of rows. With continuous aggregates (TimescaleDB) or recording rules (Prometheus), rollups are pre-computed and updated automatically as new data arrives. Queries on historical data hit the aggregate tables, not the raw data.”}},{“@type”:”Question”,”name”:”When would you use Prometheus vs TimescaleDB vs InfluxDB?”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”Prometheus: best for infrastructure monitoring (Kubernetes, servers). Pull-based scraping model, excellent ecosystem (Grafana, Alertmanager), PromQL query language. Not designed for long-term storage — use Thanos or Cortex for multi-year retention. TimescaleDB: best when you need SQL queries, existing PostgreSQL expertise, or multi-dimensional data with complex joins. InfluxDB: purpose-built TSDB, excellent for IoT and high-cardinality metrics, proprietary Flux query language. For interviews, mention any one with trade-offs.”}},{“@type”:”Question”,”name”:”What is cardinality and why does it matter in time-series systems?”,”acceptedAnswer”:{“@type”:”Answer”,”text”:”Cardinality is the number of unique tag combinations (series). High cardinality causes performance problems: a metric "request_latency" with tags {user_id, endpoint, region} could have millions of unique combinations (one series per user). Most TSDBs store indexes per series — millions of series means millions of index entries, memory exhaustion, and slow queries. Avoid using high-cardinality values (user_id, request_id) as tags. Reserve tags for bounded-cardinality dimensions: host (100s), region (10s), service (10s).”}}]}

Time-series database design and metrics infrastructure is discussed in Databricks system design interview questions.

Time-series metrics and monitoring system design is covered in Uber system design interview preparation.

Time-series database and observability system design is discussed in Netflix system design interview guide.

See also: Netflix Interview Guide 2026: Streaming Architecture, Recommendation Systems, and Engineering Excellence

See also: Scale AI Interview Guide 2026: Data Infrastructure, RLHF Pipelines, and ML Engineering

Scroll to Top