# Prometheus metriklerime user_id gibi yüksek kardinaliteli label eklemeli miyim?

> user_id gibi sınırsız bir label'ı metriğe koymayın; label'ları bounded boyutlarla sınırlayıp müşteri kırılımını exemplar, trace ya da loglara bırakın.

- Soruldu: 2026-10-14
- Yanıtlandı: 2026-10-16
- Soran: Kaya
- Etiketler: observability, metrics
- Kaynak: https://muhammetsafak.com/tr/sor-bakalim/prometheus-metriklerime-user-id-yuksek-kardinaliteli-label-eklemeliyim/
- Dil: tr-TR
- Yazar: Muhammet Şafak

---
**Soru:** Go ile yazdığım bir API'yi Prometheus ile izliyorum ve müşteri bazında latency kırılımı görmek istiyorum; hangi tenant'ın yavaşladığını p95 seviyesinde görebilmek işime çok yarayacak.

Aklımdaki en kolay yol, histogram metriklerime user_id (ya da tenant_id) label'ı eklemek. Ama binlerce müşterim var ve bunu yaparsam Prometheus'un cardinality'sini ve bellek/depolama maliyetini patlatmaktan korkuyorum. Bu label'ı eklemeli miyim, yoksa müşteri kırılımını başka bir yerde mi çözmeliyim?


Kısa cevap: Hayır — user_id'yi (ya da herhangi bir sınırsız label'ı) metriklerinize koymayın.

## Kısa cevap

Prometheus her benzersiz label kombinasyonu için ayrı bir time series tutar; müşteri başına kardinalite belleği ve maliyeti katlanarak büyütür. Müşteri kırılımını exemplar, trace veya loglara bırakın; label'ları yalnızca sınırlı (bounded) boyutlar için saklayın.

Buradaki tuzak, "bir label daha, ne olacak" diye düşünmektir. Prometheus'ta maliyet, metrik sayısıyla değil, aktif seri sayısıyla ölçülür; ve sınırsız bir label o sayıyı doğrusal değil, çarpımsal büyütür.

## Neden

1. **Kardinalite = seri sayısı.** Her benzersiz label kombinasyonu ayrı, bellekte tutulan bir time series'tir. `endpoint × status × user_id` çarpımı hızla patlar; head block'ta her seri kilobayt mertebesinde RAM demektir. Birkaç milyon seri, Prometheus'unuzu OOM'a götürür.
2. **Histogram maliyetine ayrıca dikkat.** Histogram'lar zaten çarpar: `bucket × label kombinasyonu`. 12 bucket'lı, 50 endpoint'li bir histogram, siz hiçbir şey eklemeden en az 600 seri eder (üstüne `+Inf` bucket'ı, `_sum` ve `_count` gelir). Üstüne user_id felakettir.

## Ne yapmalı

1. **Yalnızca bounded label kullanın.** İyi label'lar: `endpoint`, `method`, `status_code`, `region` — düşük ve sabit kardinaliteli. Kötü olanlar: `user_id`, `email`, `request_id`, içinde ID geçen tam URL — bunlar sınırsız, asla koymayın.
2. **Müşteri bazlı latency başka yere aittir.** Seçenekler: **exemplar** (histogram bucket'ına trace_id/user_id iliştirirsiniz — bounded, scrape başına bucket başına tek örnek), **trace** ([OTel span'ine user_id attribute'u koyup](/tr/sor-bakalim/mikroserviste-dagitik-izleme-opentelemetry-ile-nasil-kurulur/) Tempo/Jaeger'a sample edersiniz) veya **yapısal log** (Loki/ClickHouse'da toplarsınız).
   ```go
   // Bounded label'larla histogram + drill-down için exemplar:
   timer := prometheus.NewTimer(nil)
   lat := timer.ObserveDuration().Seconds()

   reqDuration.
       With(prometheus.Labels{"endpoint": ep, "status": code}).
       (prometheus.ExemplarObserver).
       ObserveWithExemplar(lat, prometheus.Labels{"trace_id": traceID})
   ```
3. **Gerçekten top-N müşteri gerekiyorsa katmanlayın.** Bounded bir `customer_tier` (free/pro/enterprise) label'ı segment kırılımını ucuza verir. Ya da bir allowlist ile yalnızca en ağır birkaç müşteriyi label'layıp gerisini `other` olarak toplayın.
4. **Koruma bandı koyun.** Scrape'te `sample_limit` tanımlayın; `prometheus_tsdb_head_series` ve `scrape_samples_scraped` üzerinde alert kurun ki kardinalite sessizce kaçmasın.
   ```promql
   # Kardinaliteyi izleyin — bu sayı sürekli tırmanıyorsa bir label kaçmıştır:
   topk(10, count by (__name__)({__name__=~".+"}))
   ```

**Sonuç:** Ben olsam metrikleri bounded label'larda (endpoint/status/tier) tutar, drill-down için histogram'a exemplar iliştirir ve gerçek müşteri bazlı analitiği log/ClickHouse tarafına atardım. "Belki lazım olur" diye eklenen yüksek kardinaliteli label'lar, 4 GB'lık bir Prometheus'u 40 GB'a çeviren şeydir. Önce sorunun trace mi yoksa metrik mi olduğuna karar verin; müşteri kırılımı neredeyse her zaman trace'in işidir.

## İlgili Yazılar

- [Servislerimi önce RED metrikleriyle mi yoksa USE metrikleriyle mi enstrümante etmeliyim?](https://muhammetsafak.com/tr/sor-bakalim/servislerimi-red-metrikleriyle-use-metrikleriyle-enstrumante-etmeliyim/) — Sor Bakalım
- [Correlation ID'yi HTTP isteğinden kuyruğa düşen job'lara kadar nasıl taşımalıyım?](https://muhammetsafak.com/tr/sor-bakalim/correlation-idyi-http-isteginden-kuyruga-dusen-joblara-kadar-nasil-tasimaliyim/) — Sor Bakalım
- [Loglarımda WARN ile ERROR seviyelerini ne zaman kullanacağıma dair net bir kural nasıl belirlerim?](https://muhammetsafak.com/tr/sor-bakalim/loglarimda-warn-ile-error-seviyelerini-ne-zaman-kullanacagima-dair-net-bir/) — Sor Bakalım
