What is High Cardinality in Observability?
High cardinality means a field or metric label has a very large number of unique values — user IDs, container IDs, request IDs — which can explode storage and query costs in many observability systems.
High cardinality describes a field, label, or dimension with a very large number of unique values. In observability, cardinality is the count of distinct time series or distinct field values a system must track — and it is the single most common reason metrics systems fall over or observability bills explode.
Low vs high cardinality
| Field | Unique values | Cardinality |
|---|---|---|
| HTTP method | ~9 | Low |
| Status code | ~60 | Low |
| Kubernetes pod name | thousands, churning | High |
| User ID | millions | Very high |
| Request/trace ID | unbounded | Extreme |
Cardinality multiplies across labels: endpoint (100) × region (20) × status (60) is already 120,000 series from three innocent-looking labels.
Why high cardinality hurts
Time-series databases like Prometheus create a separate series (with memory-resident index entries) for every unique label combination. As unique combinations grow, memory usage balloons, queries slow, and ingestion stalls — the notorious “cardinality explosion.” See Prometheus data cardinality for the mechanics. Some commercial platforms turn the same problem into pricing: charging per custom-metric series makes per-customer labels financially, rather than technically, impossible.
The tension is that high-cardinality questions are the valuable ones: which tenant, which device, which deployment caused the regression. Aggregated dashboards can’t answer them.
Managing cardinality
- Keep unbounded values (user IDs, request IDs) out of metric labels; put them in logs and traces instead, where they belong
- Aggregate or drop labels you never query, at collection time
- Use recording rules / streaming aggregation for dashboard queries
- Choose storage engines that tolerate cardinality: columnar formats index nothing by default, so a high-cardinality field costs the same as any other column
High cardinality in OpenObserve
OpenObserve stores telemetry as columnar Parquet rather than per-series indexes, so high-cardinality fields in logs, metrics, and traces don’t cause memory explosions or per-series charges — you can keep user IDs and pod names queryable without redesigning your schema around the backend’s limits.
Frequently asked questions
What is an example of high cardinality?
A metric labeled by HTTP status code has ~60 possible values — low cardinality. The same metric labeled by user ID in a system with 10 million users has 10 million possible values — high cardinality. Combined labels multiply, so user_id × endpoint × region can produce billions of unique series.
Why is high cardinality a problem for Prometheus?
Prometheus keeps an in-memory entry and a separate time series for every unique label combination. Millions of series inflate memory, slow queries, and can crash the server. This is why per-user or per-request labels are considered an anti-pattern in Prometheus.
Is high cardinality always bad?
No — high-cardinality data is exactly what you need to answer questions like "which customer is affected?". The problem is architectural — systems that index every label combination pay dearly for it, while columnar storage engines handle high-cardinality fields much more economically.
Related terms
Keep reading
See these concepts in action
OpenObserve unifies logs, metrics, traces, and frontend monitoring in one open-source platform — at a fraction of the cost of legacy tools.