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_requestsgibi isimler tercih edilmeli - Çok geniş ya da çok dar olmamalı:
metricsgibi 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,timegibi 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,buildingtag’lar (sınırlı sayıda benzersiz değer, sorgularda filtre olarak kullanılacak)temperature,humidity,pressurefield’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ı.
