Elasticsearch’te Index Oluşturma ve Mapping Tanımlama
Elasticsearch ile çalışmaya başladığınızda, verileri düzgün bir şekilde saklamak ve sorgulamak için iki temel kavramı anlamanız gerekiyor: index ve mapping. Yanlış yapılandırılmış bir index ya da eksik bir mapping tanımı, ilerleyen süreçte hem performans sorunlarına hem de beklenmedik veri kayıplarına yol açabilir. Bu yazıda, index oluşturma sürecini ve mapping tanımlamayı pratik örneklerle ele alacağız.
Index Nedir ve Neden Önemlidir?
Elasticsearch’te bir index, ilişkisel veritabanlarındaki “tablo” kavramına benzer ama aslında çok daha fazlasıdır. Bir index, benzer özelliklere sahip belgelerin (documents) saklandığı mantıksal bir birimdir. Her index kendi ayarlarına (settings) ve şemasına (mapping) sahip olabilir.
Bir e-ticaret platformu yönettiğinizi düşünün. Ürünler, siparişler, müşteriler ve log kayıtları için ayrı ayrı indexler oluşturursunuz. Bu sayede her veri tipi kendi özelliklerine göre optimize edilmiş bir yapıda saklanır.
Index oluşturmadan önce Elasticsearch cluster’ınızın sağlıklı çalıştığını kontrol etmeniz iyi bir alışkanlıktır:
curl -X GET "localhost:9200/_cluster/health?pretty"
Bu komut size cluster’ın durumunu, shard sayısını ve aktif node bilgilerini verir. status alanı green veya yellow ise devam edebilirsiniz.
Basit Bir Index Oluşturma
En basit haliyle bir index oluşturmak için tek bir PUT isteği yeterlidir:
curl -X PUT "localhost:9200/urunler?pretty"
-H 'Content-Type: application/json'
Bu komut, varsayılan ayarlarla urunler adında bir index oluşturur. Ancak bu yaklaşım production ortamlar için önerilmez çünkü Elasticsearch varsayılan olarak dynamic mapping kullanır ve ilk belgeyi eklediğinizde alan tiplerini otomatik olarak belirler. Bu bazen istemediğiniz sonuçlar doğurabilir.
Daha kontrollü bir yaklaşım için index oluştururken ayarları da belirtmek gerekir:
curl -X PUT "localhost:9200/urunler?pretty"
-H 'Content-Type: application/json'
-d '{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1,
"refresh_interval": "1s"
}
}'
Buradaki parametreler önemlidir:
- number_of_shards: Verinin kaç parçaya bölüneceğini belirler. Bu değer index oluşturulduktan sonra değiştirilemez, dikkatli seçin.
- number_of_replicas: Her shard’ın kaç kopyasının tutulacağını belirler. Node sayısına göre ayarlayın.
- refresh_interval: Yeni eklenen verilerin aranabilir hale gelmesi için gereken süre. Yoğun yazma işlemlerinde bu değeri artırabilirsiniz.
Mapping Nedir?
Mapping, bir index’teki belgelerin nasıl saklanacağını ve indexleneceğini tanımlayan şemadır. Her alanın veri tipini, analiz yöntemini ve çeşitli özelliklerini burada belirlersiniz. Doğru bir mapping tanımı, sorgu performansını doğrudan etkiler.
Elasticsearch’in desteklediği temel veri tipleri şunlardır:
- text: Tam metin araması yapılacak alanlar için. Analiz edilir, küçük harfe çevrilir, tokenize edilir.
- keyword: Tam eşleşme araması için. Filtreleme, sıralama ve aggregation’larda kullanılır.
- integer, long, float, double: Sayısal değerler için.
- date: Tarih ve zaman değerleri için.
- boolean: true/false değerleri için.
- object: İç içe geçmiş JSON nesneleri için.
- nested: Array içindeki nesneler arasındaki ilişkiyi koruyan özel bir tip.
- geo_point: Enlem/boylam koordinatları için.
Detaylı Mapping Tanımlama
Şimdi gerçek bir senaryo üzerinden gidelim. Bir e-ticaret sitesi için ürün catalog’u oluşturuyoruz:
curl -X PUT "localhost:9200/urunler?pretty"
-H 'Content-Type: application/json'
-d '{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1,
"analysis": {
"analyzer": {
"turkce_analyzer": {
"type": "custom",
"tokenizer": "standard",
"filter": ["lowercase", "asciifolding"]
}
}
}
},
"mappings": {
"properties": {
"urun_id": {
"type": "keyword"
},
"urun_adi": {
"type": "text",
"analyzer": "turkce_analyzer",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"aciklama": {
"type": "text",
"analyzer": "turkce_analyzer"
},
"fiyat": {
"type": "float"
},
"stok_adedi": {
"type": "integer"
},
"kategori": {
"type": "keyword"
},
"etiketler": {
"type": "keyword"
},
"olusturma_tarihi": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
},
"aktif": {
"type": "boolean"
},
"resimler": {
"type": "nested",
"properties": {
"url": {"type": "keyword"},
"alt_text": {"type": "text"},
"sira": {"type": "integer"}
}
}
}
}
}'
Bu örnekte birkaç önemli nokta var. urun_adi alanı hem text hem de keyword olarak tanımlandı. Bu multi-field yaklaşımı sayesinde hem tam metin araması yapabilir hem de sıralama ve aggregation işlemlerinde bu alanı kullanabilirsiniz. resimler alanı nested olarak tanımlandı çünkü her resim nesnesinin kendi içinde tutarlı bir şekilde sorgulanması gerekiyor.
Mevcut Bir Index’in Mapping’ini Görüntüleme
Var olan bir index’in mapping’ini görmek için:
curl -X GET "localhost:9200/urunler/_mapping?pretty"
Belirli bir alanın mapping bilgisini almak istiyorsanız:
curl -X GET "localhost:9200/urunler/_mapping/field/urun_adi?pretty"
Bu komutlar özellikle bir ekip arkadaşınızın oluşturduğu index’i anlamaya çalışırken veya mevcut yapıyı belgelemek istediğinizde çok işe yarar.
Dynamic Mapping Kontrolü
Elasticsearch’in dynamic mapping özelliği kolaylık sağlasa da bazen istemediğiniz durumlar yaratır. Örneğin bir sayı gelecek diye beklediğiniz alana yanlışlıkla bir string gönderilirse, Elasticsearch onu text olarak indexler ve sonraki sayısal değerler hata verir.
Dynamic mapping’i kontrol altına almanın birkaç yolu var:
curl -X PUT "localhost:9200/log_kayitlari?pretty"
-H 'Content-Type: application/json'
-d '{
"mappings": {
"dynamic": "strict",
"properties": {
"timestamp": {"type": "date"},
"level": {"type": "keyword"},
"mesaj": {"type": "text"},
"servis_adi": {"type": "keyword"},
"islem_suresi_ms": {"type": "long"}
}
}
}'
dynamic ayarının alabileceği değerler:
- true: Yeni alanları otomatik olarak ekler (varsayılan).
- false: Yeni alanları indexlemez ama kabul eder, sorgularda görünmez.
- strict: Tanımlanmamış alanlar geldiğinde hata fırlatır. Production’da kritik sistemler için tercih edilir.
Index’e Veri Eklemek ve Test Etmek
Mapping’i doğruladıktan sonra test verisi ekleyebilirsiniz:
curl -X POST "localhost:9200/urunler/_doc?pretty"
-H 'Content-Type: application/json'
-d '{
"urun_id": "PRD-001",
"urun_adi": "Kablosuz Bluetooth Kulaklık",
"aciklama": "Aktif gürültü engelleme özellikli premium kulaklık",
"fiyat": 1299.99,
"stok_adedi": 45,
"kategori": "elektronik",
"etiketler": ["kulaklık", "bluetooth", "kablosuz"],
"olusturma_tarihi": "2024-01-15 10:30:00",
"aktif": true,
"resimler": [
{
"url": "https://cdn.example.com/img/prd001-1.jpg",
"alt_text": "Kulaklık ön görünüm",
"sira": 1
}
]
}'
Veriyi ekledikten sonra mapping’e uyup uymadığını kontrol etmek için basit bir sorgu:
curl -X GET "localhost:9200/urunler/_search?pretty"
-H 'Content-Type: application/json'
-d '{
"query": {
"match": {
"urun_adi": "kulaklık"
}
}
}'
Mevcut Mapping’e Alan Ekleme
Index oluşturulduktan sonra yeni alanlar ekleyebilirsiniz. Ancak var olan bir alanın tipini değiştiremezsiniz; bu durumda reindex işlemi gerekir.
Yeni alan eklemek için:
curl -X PUT "localhost:9200/urunler/_mapping?pretty"
-H 'Content-Type: application/json'
-d '{
"properties": {
"marka": {
"type": "keyword"
},
"agirlik_kg": {
"type": "float"
},
"konum": {
"type": "geo_point"
}
}
}'
Bu işlem mevcut verileri etkilemez, sadece şemanıza yeni alanlar ekler.
Index Template Kullanımı
Eğer benzer yapıda birden fazla index oluşturuyorsanız, her seferinde aynı mapping’i yazmak yorucu olabilir. Index template‘ler bu sorunu çözer. Özellikle log yönetimi yapıyorsanız ve her gün yeni bir index oluşturuyorsanız template’ler hayat kurtarır.
curl -X PUT "localhost:9200/_index_template/log_template?pretty"
-H 'Content-Type: application/json'
-d '{
"index_patterns": ["uygulama-log-*"],
"priority": 1,
"template": {
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0,
"refresh_interval": "5s"
},
"mappings": {
"dynamic": "false",
"properties": {
"@timestamp": {
"type": "date"
},
"log_level": {
"type": "keyword"
},
"mesaj": {
"type": "text"
},
"servis": {
"type": "keyword"
},
"kullanici_id": {
"type": "keyword"
},
"istek_suresi": {
"type": "integer"
}
}
}
}
}'
Artık uygulama-log-2024-01, uygulama-log-2024-02 gibi bir isimle oluşturulan tüm indexler bu template’i otomatik olarak kullanacak.
Reindex: Mapping Değiştirme İhtiyacında Kurtarıcı
Var olan bir alanın tipini değiştirmeniz gerektiğinde reindex işlemi yapmanız şarttır. Örneğin fiyat alanını float‘tan double‘a çevirmek istiyorsunuz:
# Önce yeni mapping ile hedef index oluşturun
curl -X PUT "localhost:9200/urunler_v2?pretty"
-H 'Content-Type: application/json'
-d '{
"mappings": {
"properties": {
"fiyat": {
"type": "double"
}
}
}
}'
# Sonra mevcut veriyi kopyalayın
curl -X POST "localhost:9200/_reindex?pretty"
-H 'Content-Type: application/json'
-d '{
"source": {
"index": "urunler"
},
"dest": {
"index": "urunler_v2"
}
}'
Büyük indexlerde reindex işlemi uzun sürebilir. Bu süreçte eski index aktif kalmaya devam eder, işlem bitince alias’ı yeni index’e yönlendirirsiniz. Bu nedenle production’da indexlere doğrudan isimle değil, alias üzerinden erişmek büyük kolaylık sağlar.
Index Alias Kullanımı
Alias’lar, index yönetimini çok daha esnek hale getirir. Reindex sonrası geçiş yaparken downtime yaşamazsınız:
curl -X POST "localhost:9200/_aliases?pretty"
-H 'Content-Type: application/json'
-d '{
"actions": [
{
"remove": {
"index": "urunler",
"alias": "urunler_aktif"
}
},
{
"add": {
"index": "urunler_v2",
"alias": "urunler_aktif"
}
}
]
}'
Bu komut atomik olarak çalışır. Eski index’ten alias’ı kaldırıp yeni index’e ekler. Uygulamanız her zaman urunler_aktif alias’ını kullandığından, bu geçişten etkilenmez.
Sık Yapılan Hatalar ve Çözümleri
Günlük operasyonlarda karşılaştığım bazı yaygın hatalar ve bunların çözümleri şöyle:
Mapping conflict hatası: Bir alana önce string sonra sayı gönderdiğinizde oluşur. Dynamic mapping kapatın veya strict mod kullanın.
Too many fields hatası: Elasticsearch varsayılan olarak bir index’te en fazla 1000 alan tanımlanmasına izin verir. Bu sınıra takılırsanız mapping’inizi gözden geçirin ya da şu ayarı değiştirin:
curl -X PUT "localhost:9200/urunler/_settings?pretty"
-H 'Content-Type: application/json'
-d '{
"index.mapping.total_fields.limit": 2000
}'
Date parsing hatası: Tarih formatlarında tutarsızlık varsa bu hatayla karşılaşırsınız. Mapping’de birden fazla format tanımlayabilirsiniz, yukarıdaki olusturma_tarihi örneğinde bunu gösterdik.
Shard boyutu dengesizliği: Tek bir shard çok büyüdüğünde (30-50 GB’ı aşınca) performans düşer. Index oluşturma aşamasında shard sayısını verinin büyüklüğüne göre hesaplayın. Genel kural olarak her shard için 20-40 GB öngörülür.
Index Ayarlarını Dinamik Olarak Güncelleme
Bazı index ayarları oluşturulduktan sonra değiştirilebilir. Replica sayısını veya refresh interval’ı güncellemek bunların başında gelir:
curl -X PUT "localhost:9200/urunler/_settings?pretty"
-H 'Content-Type: application/json'
-d '{
"number_of_replicas": 2,
"refresh_interval": "30s"
}'
Büyük miktarda veri indexliyorsanız ve gerçek zamanlı arama gerekli değilse refresh_interval‘ı artırmak yazma performansını önemli ölçüde iyileştirir. Toplu veri aktarımı sırasında bu değeri -1 yaparak refresh’i tamamen devre dışı bırakabilir, işlem bitince eski değerine döndürebilirsiniz.
Index’i Açma ve Kapatma
Kullanılmayan ama silmek istemediğiniz indexleri kapatabilirsiniz. Kapalı indexler memory tüketmez ama disk alanı kaplar:
# Index'i kapat
curl -X POST "localhost:9200/urunler_arsiv/_close?pretty"
# Index'i tekrar aç
curl -X POST "localhost:9200/urunler_arsiv/_open?pretty"
Index Silme
Artık ihtiyaç duymadığınız indexleri silmek için:
curl -X DELETE "localhost:9200/urunler_test?pretty"
Wildcard ile birden fazla index silebilirsiniz, ancak bu işlemi production’da yaparken çok dikkatli olun:
# TEST ortamında - dikkatli kullanın!
curl -X DELETE "localhost:9200/test-*?pretty"
Yanlışlıkla kritik bir index’i silmemek için Elasticsearch’te action.destructive_requires_name ayarını true yapmanızı öneririm. Bu ayar aktifken wildcard ile index silinemez.
Sonuç
Elasticsearch’te index oluşturma ve mapping tanımlama, sağlam bir veri yönetimi altyapısının temel taşlarıdır. Bu süreçte dikkat edilmesi gereken kritik noktaları özetleyelim:
- Index oluştururken shard sayısını önceden iyi hesaplayın, sonradan değiştiremezsiniz.
- Production ortamlarında dynamic mapping yerine explicit mapping tanımlayın.
- Hem tam metin araması hem de filtreleme gereken alanlar için multi-field yaklaşımını kullanın.
- Benzer yapıdaki indexler için template’leri hayatınıza sokun.
- Uygulamalarınızın index isimlerine değil alias’lara bağlanmasını sağlayın, bu size operasyonel esneklik kazandırır.
- Mapping değişikliği gerektiğinde reindex işlemini alias geçişiyle birlikte planlayın.
Bu temelleri sağlam oturtursanız, Elasticsearch cluster’ınız hem performanslı hem de yönetilebilir olmaya devam eder. Sorgu optimizasyonu ve aggregation konularına geçmeden önce mapping’in doğru kurulmuş olması şarttır; sonradan yapılan düzeltmeler her zaman daha maliyetli olur.
