InfluxDB Veri Modeli: Measurement, Tag ve Field Kavramları

Zaman serisi veritabanları arasında en çok kullanılanlardan biri olan InfluxDB, özellikle monitoring, IoT ve metrik toplama gibi alanlarda neredeyse standart haline geldi. Ama InfluxDB’yi düzgün kullanabilmek için önce onun veri modelini kafanda oturtman gerekiyor. Çoğu kişi InfluxDB’yi kuruyor, Grafana’ya bağlıyor, birkaç metrik yazıyor ve iş bitiyor sanıyor. Sonra sorgular yavaşlamaya başlıyor, kardinalite patlaması yaşanıyor, ya da yanlış tag/field seçimi yüzünden asla çalışmayan sorgular yazıyorlar. Bu yazıda InfluxDB’nin temel veri modelini, yani measurement, tag ve field kavramlarını derinlemesine ele alacağız.

InfluxDB Veri Modeli Nedir?

InfluxDB, klasik ilişkisel veritabanlarından köklü biçimde farklı bir veri modeli kullanır. İlişkisel bir veritabanında tablo, satır ve sütun varken; InfluxDB’de measurement, tag, field ve timestamp var. Bu dört kavram, InfluxDB’deki her veri noktasını tanımlar.

Genel yapıyı şöyle düşünebilirsin: Measurement bir SQL tablosuna benzer ama çok daha esnek. Her veri noktası bir timestamp, bir ya da birden fazla tag, bir ya da birden fazla field değeri içerir.

Bir veri noktasının satır protokolü (line protocol) formatındaki yapısı şöyle:

measurement_name,tag_key=tag_value field_key=field_value timestamp

Örnek vermek gerekirse:

cpu_usage,host=web01,region=eu-west cpu_percent=72.4,load_avg=1.8 1699900800000000000

Bu tek satırda ne var? cpu_usage measurement adı, host ve region tag’lar, cpu_percent ve load_avg field’lar, sondaki büyük sayı ise nanosaniye cinsinden Unix timestamp.

Measurement: Verinin Kategorisi

Measurement, InfluxDB’de verileri gruplamak için kullanılan en üst seviye kavramdır. SQL’deki tablo ile kıyaslanabilir ama şema esnekliği çok daha yüksektir. Aynı measurement içinde farklı tag ve field kombinasyonları olabilir.

İyi bir measurement tasarımı için şunlara dikkat etmek gerekir:

  • Measurement isimleri anlamlı ve tutarlı olmalı: cpu_usage, memory_stats, http_requests gibi isimler tercih edilmeli
  • Çok geniş ya da çok dar olmamalı: metrics gibi her şeyi koyan bir measurement kötü tasarımdır; ama her host için ayrı measurement açmak da aynı derecede kötüdür
  • Dinamik measurement isimleri kullanmaktan kaçın: Measurement adını runtime’da oluşturmak kardinalite sorunlarına yol açar

Measurement Oluşturma ve Veri Yazma

InfluxDB’de measurement’lar önceden oluşturulmaz. İlk veri yazıldığında otomatik olarak oluşur. Bunu bir terminal üzerinden test edelim:

# InfluxDB CLI ile bağlan
influx -host localhost -port 8086

# Veya InfluxDB v2 için
influx write 
  --bucket mydb 
  --precision ns 
  "cpu_usage,host=web01,region=eu-west cpu_percent=72.4 $(date +%s%N)"
# HTTP API kullanarak veri yaz (InfluxDB v1)
curl -i -XPOST 'http://localhost:8086/write?db=mydb' 
  --data-binary 'cpu_usage,host=web01,region=eu-west cpu_percent=72.4,load_avg=1.8'

Measurement içindeki verilere bakmak için:

# InfluxDB v1 - InfluxQL
influx -execute "SELECT * FROM cpu_usage WHERE time > now() - 1h" -database mydb

# Measurement listesini görmek için
influx -execute "SHOW MEASUREMENTS" -database mydb

Tag’lar: Metadata ve İndeksleme

Tag’lar InfluxDB’nin en kritik ve en çok yanlış anlaşılan bileşenidir. Tag’lar string tipinde key-value çiftleridir ve otomatik olarak indekslenir. Bu iki özellik birlikte çok önemli bir anlam taşır.

Tag’ların indekslenmesi, bu alanlara göre yapılan filtreleme işlemlerinin çok hızlı olduğu anlamına gelir. WHERE host='web01' gibi bir sorgu tag üzerinden yapılıyorsa, InfluxDB inverted index kullanarak sonuca çok hızlı ulaşır.

Tag Kullanım Örnekleri

Gerçek dünya senaryosu olarak bir web sunucu izleme sistemi düşünelim:

# Sunucu metriklerini yaz
curl -i -XPOST 'http://localhost:8086/write?db=monitoring' --data-binary '
http_requests,method=GET,status=200,host=web01,datacenter=ist1 count=1420,response_time=145.2
http_requests,method=POST,status=201,host=web01,datacenter=ist1 count=83,response_time=312.7
http_requests,method=GET,status=404,host=web02,datacenter=ist1 count=12,response_time=45.1
'

Bu örnekte method, status, host ve datacenter tag olarak tanımlanmış. Neden? Çünkü bunlar:

  • Sabit sayıda benzersiz değere sahip (GET, POST, PUT, DELETE gibi)
  • Sorgularda WHERE veya GROUP BY ile sıkça kullanılacak
  • String değerler (sayısal olmayan)

Kardinalite Problemi

InfluxDB’de en yaygın performans sorunu yüksek kardinalitedir. Kardinalite, bir tag’ın kaç farklı benzersiz değer alabileceğini ifade eder. Tag kombinasyonlarının çarpımı, measurement’ın toplam kardinalitesini verir.

Şunu asla yapma:

# YANLIS: IP adresini tag olarak kullanmak
http_requests,client_ip=192.168.1.105,host=web01 count=1

# YANLIS: UUID ya da session ID'yi tag olarak kullanmak  
user_events,session_id=a3f2b1c9-... page_view=1

# YANLIS: Unix timestamp'i tag olarak kullanmak
cpu_usage,snapshot_time=1699900800 cpu_percent=72.4

Bu tür kullanımlar milyonlarca farklı tag değeri yaratır ve InfluxDB’nin in-memory index’i şişer. Bellek tükenir, sorgular yavaşlar, sistem çöker.

Doğru yaklaşım:

# DOGRU: IP adresini field olarak sakla, sorgulama gerekmiyorsa
http_requests,host=web01,region=eu-west count=1,client_ip="192.168.1.105"

# DOGRU: Sınırlı sayıda değer alan alanları tag yap
http_requests,method=GET,status_class=2xx,host=web01 count=1420

Tag Key ve Value Kuralları

Tag key ve value’larda dikkat edilmesi gereken bazı önemli noktalar var:

  • Tag value’lar her zaman string olarak saklanır
  • Boşluk, virgül ve eşittir işareti kaçırılmalıdır (escape edilmeli)
  • Boş string tag value olarak kullanılabilir ama tavsiye edilmez
  • Tag key olarak _measurement, _field, time gibi rezerve isimler kullanılamaz
# Boşluk içeren tag value için kaçırma
temperature,location=data center floor1 value=22.5

# Özel karakter içeren tag value
server_metrics,host=web,prod,01 cpu=45.2

Field’lar: Asıl Veri

Field’lar, ölçülen gerçek değerlerin saklandığı yerdir. Tag’ların aksine field’lar indekslenmez ve sayısal (integer, float), string veya boolean tipte olabilir.

Field’ları tag’lardan ayıran temel özellikler:

  • İndekslenmez: Field değerine göre filtreleme yapmak zorunda kalırsan sorgu tüm veriyi tarar
  • Zorunludur: Bir veri noktasında en az bir field olması şarttır
  • Çoklu tip desteği: Float, integer, string, boolean desteklenir

Field Tiplerine Göre Yazım

# Float (varsayılan sayısal tip)
server_stats,host=db01 cpu_percent=78.5,memory_percent=62.1

# Integer (i suffix ile)
server_stats,host=db01 active_connections=142i,open_files=1024i

# Boolean
service_health,service=nginx,host=web01 is_running=true,ssl_valid=false

# String (tırnak içinde)
error_log,host=app01,level=error message="Connection timeout after 30s"

Field’larda Tip Uyuşmazlığı Sorunu

InfluxDB’de bir field’ın tipi ilk yazıldığında belirlenir ve sonradan değiştirilemez. Bu, production ortamlarda can sıkıcı sorunlara yol açabilir:

# Ilk yazim - float olarak belirlendi
temperature,sensor=s01 value=22.5

# Sonraki yazim - integer gönderirsen hata alirsin
temperature,sensor=s01 value=23i
# Hata: field type conflict: input field "value" on measurement "temperature" is type integer, already exists as type float

Bu durumu çözmek için ya field adını değiştirmen ya da measurement’ı silip yeniden oluşturman gerekir. Bu yüzden veri tiplerini baştan doğru belirlememek büyük bir baş ağrısıdır.

# Tip durumunu kontrol et
influx -execute "SHOW FIELD KEYS FROM temperature" -database mydb

# Tip çakışmasını field rename ile çöz
# Yeni isimle yaz
temperature,sensor=s01 value_float=22.5

Gerçek Dünya Senaryosu: IoT Sensör İzleme

Bir fabrikada 500 adet sıcaklık, nem ve basınç sensörü olduğunu düşün. Bu sensörlerden gelen verileri InfluxDB’de nasıl modelleyeceğini inceleyelim.

Kötü Tasarım

# YANLIS tasarim
temp_sensor_001,reading_type=temperature value=23.4
temp_sensor_001,reading_type=humidity value=65.2
temp_sensor_002,reading_type=temperature value=24.1
# Her sensör için ayrı measurement -> 500 measurement

Ya da:

# YANLIS tasarim - sensor_id'yi tag yapman iyi ama okuma tipini field yapmak sorgu performansini düşürür
sensor_readings,sensor_id=001 temperature=23.4,humidity=65.2,pressure=1013.2,floor=3,line=B
# floor ve line field yerine tag olmali!

Doğru Tasarım

# DOGRU tasarim
sensor_readings,sensor_id=S001,floor=3,production_line=B,building=main temperature=23.4,humidity=65.2,pressure=1013.2

sensor_readings,sensor_id=S002,floor=1,production_line=A,building=main temperature=24.1,humidity=61.8,pressure=1014.5

sensor_readings,sensor_id=S003,floor=3,production_line=B,building=warehouse temperature=19.2,humidity=55.3,pressure=1012.8

Bu tasarımda:

  • sensor_id, floor, production_line, building tag’lar (sınırlı sayıda benzersiz değer, sorgularda filtre olarak kullanılacak)
  • temperature, humidity, pressure field’lar (ölçülen gerçek değerler, her ölçümde farklı)

Toplu Veri Yazma

Production ortamında tek tek değil toplu yazım tercih edilmeli:

# Birden fazla satiri tek seferde gönder
curl -i -XPOST 'http://localhost:8086/write?db=factory_iot' 
  --data-binary 
  'sensor_readings,sensor_id=S001,floor=3,production_line=B temperature=23.4,humidity=65.2,pressure=1013.2
sensor_readings,sensor_id=S002,floor=1,production_line=A temperature=24.1,humidity=61.8,pressure=1014.5
sensor_readings,sensor_id=S003,floor=3,production_line=B temperature=19.2,humidity=55.3,pressure=1012.8'

Timestamp Yönetimi

InfluxDB’de timestamp nanosaniye cinsinden Unix timestamp olarak saklanır. Timestamp belirtilmezse sunucu kendi zamanını kullanır. Production’da timestamp’i her zaman kendin belirtmen önerilir, çünkü ağ gecikmesi veya batch yazım durumlarında sunucu zamanı yanıltıcı olabilir.

# Saniye cinsinden timestamp ile yazim (precision=s)
curl -i -XPOST 'http://localhost:8086/write?db=mydb&precision=s' 
  --data-binary 'cpu_usage,host=web01 cpu_percent=72.4 1699900800'

# Milisaniye cinsinden (precision=ms)
curl -i -XPOST 'http://localhost:8086/write?db=mydb&precision=ms' 
  --data-binary 'cpu_usage,host=web01 cpu_percent=72.4 1699900800000'

# Nanosaniye (varsayilan)
curl -i -XPOST 'http://localhost:8086/write?db=mydb' 
  --data-binary 'cpu_usage,host=web01 cpu_percent=72.4 1699900800000000000'

Sorgulama: Tag ve Field Farklarını Pratikte Görmek

InfluxQL ile çalışırken tag ve field arasındaki fark sorgularda açıkça ortaya çıkar:

# Tag uzerinden gruplama (hizli, indeksli)
influx -execute "SELECT mean(cpu_percent) FROM cpu_usage WHERE time > now() - 1h GROUP BY host, region" -database mydb

# Tag uzerinden filtreleme (hizli)
influx -execute "SELECT * FROM cpu_usage WHERE host='web01' AND time > now() - 30m" -database mydb

# Field uzerinden filtreleme (yavas, full scan)
influx -execute "SELECT * FROM cpu_usage WHERE cpu_percent > 90 AND time > now() - 1h" -database mydb

# Mevcut tag'lari listele
influx -execute "SHOW TAG KEYS FROM cpu_usage" -database mydb

# Belirli bir tag'in degerlerini gör
influx -execute "SHOW TAG VALUES FROM cpu_usage WITH KEY = host" -database mydb

InfluxDB v2 Flux ile Sorgulama

# Flux dili ile sorgu (v2)
influx query '
from(bucket: "monitoring")
  |> range(start: -1h)
  |> filter(fn: (r) => r._measurement == "cpu_usage")
  |> filter(fn: (r) => r.host == "web01")
  |> filter(fn: (r) => r._field == "cpu_percent")
  |> aggregateWindow(every: 5m, fn: mean)
'

Schema Tasarımı için En İyi Pratikler

Yıllar içinde edindiğim deneyimlerden derlediğim kurallar:

  • Bir kuralı kafana yaz: “Sorgularda WHERE veya GROUP BY ile kullanacaksam tag, sadece okuyacaksam field”
  • Tag value sayısını takip et: Toplam kardinalite 10 milyonun üzerine çıktığında ciddi performans sorunları başlar
  • Measurement başına field sayısını makul tut: 20-30 field genellikle iyi bir sınır
  • Boolean field’lar için dikkatli ol: true/false’u tag yapma, kardinaliteyi patlatamazsın ama gruplamalar gereksiz olur; boolean değerler genellikle field olarak daha mantıklı
  • Zaman serisi olacak verileri sakın başka bir yerde tutma: Eğer bir değer zamanla değişiyorsa InfluxDB’ye, değişmiyorsa (metadata) bir ilişkisel DB veya key-value store’a koy

Kardinalite İzleme

# Measurement bazinda kardinalite kontrolü
influx -execute "SHOW SERIES EXACT CARDINALITY ON mydb FROM cpu_usage" -database mydb

# Tum veritabani kardinalitesi
influx -execute "SHOW SERIES EXACT CARDINALITY ON mydb" -database mydb

# v2 için
influx v1 shell
> SHOW STATS FOR 'indexes'

Eğer kardinalite 1 milyonu geçmeye başladıysa acil aksiyon alman gerekiyor. Önce hangi tag’ın soruna yol açtığını bul, sonra o tag’ı field’a dönüştür ya da veri modelini yeniden tasarla.

Retention Policy ve Veri Organizasyonu

Veri modelinin önemli bir parçası da verilerin ne kadar süre tutulacağı. Tag tasarımını yaparken retention policy ile birlikte düşünmek gerekir:

# Retention policy oluştur (v1)
influx -execute "CREATE RETENTION POLICY short_term ON mydb DURATION 7d REPLICATION 1 DEFAULT" -database mydb

influx -execute "CREATE RETENTION POLICY long_term ON mydb DURATION 365d REPLICATION 1" -database mydb

# Ham veri için short_term, downsampled veri için long_term kullan
# Bu sayede disk kullanimi optimize edilir

# Continuous query ile downsampling (v1)
influx -execute "CREATE CONTINUOUS QUERY cq_cpu_hourly ON mydb BEGIN SELECT mean(cpu_percent) INTO long_term.cpu_hourly FROM cpu_usage GROUP BY time(1h), host, region END" -database mydb

Bu yaklaşımla ham 1-dakikalık veriyi 7 gün tutarken, 1-saatlik aggregate veriyi 1 yıl boyunca saklayabilirsin. Tag seçimini burada da doğru yapman kritik; CQ sorgularında hangi tag’larla GROUP BY yapacaksın, bunu önceden planla.

Sonuç

InfluxDB’nin veri modeli ilk bakışta basit görünüyor ama measurement, tag ve field arasındaki farkı ve bu farkın arkasındaki teknik nedenleri anlamadan iyi bir schema tasarımı yapmak mümkün değil. Yanlış bir tag seçimi başlangıçta fark edilmez, ama veri birikmeye başladığında sistemi felç edebilir.

Özetlemek gerekirse:

  • Measurement: Verilerin mantıksal kategorisi, SQL tablosuna benzer
  • Tag: İndeksli, string, düşük kardinaliteli metadata; filtreleme ve gruplama için
  • Field: İndekssiz, çoklu tip destekli, asıl ölçüm değerleri
  • Timestamp: Nanosaniye hassasiyetinde, her veri noktasının zamanı

En çok yapılan hataları tekrarlayayım: IP adresi, UUID, session ID gibi yüksek kardinaliteli değerleri tag yapmak, tip uyuşmazlığına yol açacak şekilde aynı field’a farklı tipte veri yazmak, ve her şeyi tek bir measurement’a tıkmak.

Bir schema tasarlarken kendin şunu sor: Bu veriyle 6 ay sonra hangi sorguları çalıştıracağım? O sorguları zihninde simüle et, hangi field WHERE’de kullanılacak, hangisi GROUP BY’da? Cevapların seni doğru tag/field ayrımına götürecektir. Ve kardinaliteyi her zaman göz önünde tut; güzel bir monitoring sistemi, kontrolsüz büyüyen bir index yüzünden yerle bir olmamalı.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir