Glossary · T

Time Series

A sequence of timestamped values for one metric and one unique combination of labels, the basic unit of metrics storage.

By Obseria Editorial TeamUpdated

Definition

A time series is an ordered sequence of samples, each a timestamp and a value, that belongs to one metric and one unique set of labels. In monitoring, every distinct combination of metric name and label values forms its own time series, which a time series database stores, compresses and queries over time windows.

What is a time series?

A time series is a list of measurements of the same thing, taken over time and kept in time order. In observability, the "thing" is a metric for one specific combination of dimensions: the request count of the checkout service, in region eu-central-1, for status code 500. Each measurement is a sample that consists of a timestamp and a numeric value.

Time series are the basic unit in which monitoring systems store and query metrics. Dashboards, alert rules, SLO calculations and anomaly detection all operate on time series.

The time series data model

The data model popularized by Prometheus and used, with small differences, by OpenTelemetry and most metrics backends has three parts:

  • Metric name, for example http_server_requests_total, describing what is measured.
  • Labels (OpenTelemetry calls them attributes), key-value pairs such as service="checkout" or status="500".
  • Samples, each a timestamp and a value. In Prometheus the value is a 64-bit float and the timestamp has millisecond precision.

The metric name plus the full set of label values is the series identity. Change any label value and you have a different time series:

http_server_requests_total{service="checkout", region="eu-central-1", status="200"}
http_server_requests_total{service="checkout", region="eu-central-1", status="500"}

These are two separate series, each with its own stream of samples. In OpenTelemetry, resource attributes such as service.name and the metric's own attributes together play this role, and each data point additionally carries an aggregation temporality, cumulative or delta.

Why cardinality matters

The number of distinct time series is called cardinality. It grows multiplicatively: a metric with 20 services, 3 regions, 10 endpoints and 5 status codes can produce up to 20 × 3 × 10 × 5 = 3,000 series. Adding a label like user_id with 100,000 values could turn that into hundreds of millions. Every active series costs memory for its index and storage for its samples, so high cardinality is the most common reason metrics systems become slow or expensive. Identifiers with unbounded values such as user IDs, request IDs or trace IDs belong in logs and traces, not in metric labels.

How time series are stored

Time series databases (TSDBs) are optimized for an append-heavy, read-by-range workload. Samples of one series arrive in time order and are usually queried as a range, so TSDBs group them in chunks and compress them aggressively, for example with delta-of-delta encoding for timestamps and XOR encoding for float values, techniques described in Facebook's Gorilla paper. Older data is often reduced with downsampling, keeping for instance five-minute averages, minima and maxima instead of every raw sample, and eventually deleted according to retention rules.

Querying time series

Queries select series by name and labels, then aggregate them over time and across dimensions. A typical PromQL example computes the error ratio per service over the last five minutes:

sum by (service) (rate(http_server_requests_total{status=~"5.."}[5m]))
/
sum by (service) (rate(http_server_requests_total[5m]))

Two steps happen here: rate() turns each cumulative counter series into a per-second rate, and sum by (service) collapses all regions, endpoints and status codes into one series per service.

Best practices

  • Choose labels with a small, bounded set of values and keep naming consistent across services.
  • Prefer counters and histograms over pre-computed averages; they aggregate correctly across series.
  • Pick a scrape or export interval that fits the signal, typically 10 to 60 seconds.
  • Monitor the number of active series per metric to catch cardinality growth early.
  • Drop or aggregate unused series at ingest instead of paying to store them.

Counters, gauges and histograms

What a sample value means depends on the metric type behind the series:

  • Counters only go up, for example total requests or bytes sent. You almost always query their rate of change, not the raw value, and backends handle counter resets after restarts.
  • Gauges go up and down and represent a current state, such as memory in use, queue depth or temperature.
  • Histograms count observations in buckets, such as request durations. A classic Prometheus histogram is stored as one series per bucket plus a sum and a count series, which is why histograms can add many series quickly.

Knowing the type is essential for correct aggregation: summing gauges across instances is usually fine, averaging precomputed percentiles is not.

How Obseria helps

Obseria ingests metrics via OpenTelemetry and Prometheus and lets you query them with PromQL, OQL or SQL. Metric data points are retained for 13 months, which covers year-over-year comparisons. Obseria Pipeline can filter, aggregate or drop labels at ingest to keep cardinality under control, and filtered data only incurs ingestion cost. Built-in anomaly detection learns the normal pattern of each time series and highlights deviations.

Frequently asked questions

A metric is the named measurement, such as http_server_requests_total. A time series is one stream of samples for that metric with one specific combination of label values. One metric usually produces many time series.

A sample is a single data point: a timestamp and a numeric value. In Prometheus the value is a 64-bit float and the timestamp has millisecond precision.

A time series database (TSDB) is a database optimized for writing timestamped samples in order and reading them by time range. It indexes series by name and labels and compresses samples heavily to keep storage efficient.

Every distinct label value creates a new time series. Unbounded values such as user IDs or request IDs multiply the number of series, increase memory and cost and slow queries. Record them in logs or traces instead.

Put these concepts into practice

Try Obseria free for 14 days, with OpenTelemetry and no proprietary agents.