Overview
Time-series databases such as Prometheus, InfluxDB and TimescaleDB exploit three facts: writes are appends, queries are time ranges, and consecutive values are similar. Storage is shaped around those facts.
How it works
Data is split into time chunks, compressed, and rolled up as it ages.
raw 10s points -> keep 7 days
1m rollups -> keep 90 days
1h rollups -> keep 2 years
-- TimescaleDB style
SELECT create_hypertable('cpu', 'ts', chunk_time_interval => INTERVAL '1 day');
SELECT add_retention_policy('cpu', INTERVAL '30 days');
Dropping a chunk is a file deletion, far cheaper than deleting rows from a big table.
Query shape determines design. Most dashboards ask for a rate or percentile over the last hour for a few series, and alerts evaluate the same expressions repeatedly, so recent chunks are kept in memory and indexed by label sets. Long-range queries read the rolled-up tiers instead of raw data. When you need exact percentiles over time, store histogram buckets or sketches rather than precomputed percentiles, because percentiles cannot be combined across series or windows by averaging.
Pitfalls
- Using high-cardinality values (request IDs, user IDs) as labels or tags.
- Choosing chunk sizes so large that recent chunks no longer fit in memory.
- Keeping raw resolution forever instead of downsampling.
- Averaging percentiles during rollup, which gives meaningless results; store histograms instead.
Key takeaways
- Time-partitioning makes ingest, queries and retention cheap.
- Cardinality is the main scaling limit.
- Downsample old data and keep raw data short.
- Do not use it for general relational workloads.