Veritabanı Yönetimi

    Elasticsearch Arama Optimizasyonu

    Elasticsearch sorgularını hızlandırmanın ve sonuç kalitesini artırmanın pratik yolları.

    13 dk okuma Güncellendi: 25 Ağustos 2026

    Elasticsearch arama optimizasyonu iki ayrı problemi birden kapsar ve bunları karıştırmak, çabanızın boşa gitmesinin en yaygın sebebidir. Birincisi hız: sorgu neden 800 milisaniye sürüyor, nasıl 40 milisaniyeye indiririm? İkincisi alaka: kullanıcı "kabin fanı" arattığında neden alakasız ürünler üstte çıkıyor? Bu iki problemin çözümleri farklı yerlerde durur — hız çoğunlukla sorgu bağlamı, sayfalama ve parça tasarımıyla ilgilidir, alaka ise analiz zinciri ve puanlama ayarlarıyla.

    Bu rehberde ikisini de sırayla ele alacağım. Filtre bağlamının neden neredeyse bedava olduğunu, hangi sorgu tipinin nerede kullanılacağını, _analyze API'siyle "neden bu belge bulunmuyor" sorusunun nasıl cevaplanacağını, alaka puanını nasıl yöneteceğinizi, otomatik tamamlamanın doğru kurulumunu, derin sayfalamanın neden çöktüğünü ve profil çıkarma ile yavaş sorgu günlüğünü göreceğiz. Kurulumu henüz yapmadıysanız önce Elasticsearch kurulumu yazısındaki adımları tamamlayın.

    Sorgu Bağlamı ve Filtre Bağlamı#

    Elasticsearch'te bir koşul iki farklı bağlamda çalışabilir ve aradaki fark, tek başına en büyük performans kazancını verir. Sorgu bağlamı (must, should) her belge için bir alaka puanı hesaplar; bu hesap maliyetlidir ve sonucu önbelleklenemez. Filtre bağlamı (filter, must_not) puan hesaplamaz, yalnızca "evet/hayır" der ve sonucu düğüm düzeyinde önbelleğe alınır.

    Kural nettir: puanı etkilemesi gerekmeyen her koşul filtreye gider. Fiyat aralığı, kategori, stok durumu, tarih aralığı, dil — bunların hiçbiri "ne kadar alakalı" sorusuna cevap vermez, sadece sonucu daraltır.

    # KÖTÜ: her koşul puanlanıyor, hiçbiri önbelleklenmiyor
    {
      "query": {
        "bool": {
          "must": [
            { "match": { "baslik": "kabin fanı" } },
            { "term":  { "kategori": "aksesuar" } },
            { "range": { "fiyat": { "lte": 500 } } },
            { "term":  { "stokta": true } }
          ]
        }
      }
    }
    
    # İYİ: yalnızca metin sorgusu puanlanıyor, geri kalanı önbellekli filtre
    {
      "query": {
        "bool": {
          "must":   [ { "match": { "baslik": "kabin fanı" } } ],
          "filter": [
            { "term":  { "kategori": "aksesuar" } },
            { "range": { "fiyat": { "lte": 500 } } },
            { "term":  { "stokta": true } }
          ]
        }
      }
    }
    

    İkinci sürüm aynı sonucu döner ama tekrar eden filtreler önbellekten karşılandığı için, yoğun kullanılan bir kategori sayfasında fark on kata varabilir. Aynı mantığın devamı olarak, sonuç sayısı dışında bir şeye ihtiyacınız yoksa "size": 0 gönderin; Elasticsearch o zaman belge gövdelerini hiç toplamaz ve toplama (aggregation) sonuçlarını parça istek önbelleğine alır.

    Doğru Sorgu Tipini Seçmek#

    Yanlış sorgu tipi, hem yavaşlık hem de alakasız sonuç üretir. Aşağıdaki tablo hangi durumda hangisini kullanacağınızı özetliyor.

    SorguNe yaparKullanılacak alan tipiTipik yer
    matchMetni analiz eder, kelime bazlı arartextSerbest arama kutusu
    termAnaliz etmez, tam eşleşme ararkeywordKategori, kimlik, durum
    termsBirden çok tam değerkeywordÇoklu seçim filtresi
    match_phraseKelime sırasını korurtextTırnak içinde arama
    multi_matchBirden çok alanda arartextBaşlık + açıklama birlikte
    rangeAralıksayı, tarihFiyat, tarih filtresi
    prefix / wildcardÖnek/jokerkeywordDikkatli kullanın, pahalı
    existsAlan dolu muhepsiEksik veri kontrolü

    En sık yapılan hata term sorgusunu text alanda kullanmaktır ve sonucu kafa karıştırıcıdır: hiçbir şey dönmez. Sebebi şudur — text alan index'lenirken analiz edilip küçük harfe çevrilmiş kelimelere bölünür, ama term sorgusu verdiğiniz değeri analiz etmez. Yani index'te kabin ve fanı tokenları varken siz "Kabin Fanı" ararsınız ve eşleşme olmaz.

    Çoklu alan aramasında multi_match tipini bilinçli seçin. best_fields (varsayılan) en iyi eşleşen tek alanın puanını alır, most_fields tüm alanların puanını toplar, cross_fields ise alanları tek bir büyük alan gibi ele alır — ad-soyad gibi bölünmüş verilerde doğru olan budur:

    {
      "query": {
        "multi_match": {
          "query": "kabin fanı 120",
          "type": "best_fields",
          "fields": [ "baslik^3", "aciklama", "marka" ],
          "tie_breaker": 0.3
        }
      }
    }
    

    baslik^3 yazımı, başlıktaki eşleşmenin açıklamadakinden üç kat değerli olduğunu söyler. Bu tek satır, alaka kalitesini en hızlı iyileştiren müdahaledir; ölçmeden büyük değerler vermeyin, 2 ile 5 arası genellikle yeterlidir.

    Analiz Zincirini Anlamak ve Doğrulamak#

    "Bu belgeyi neden bulamıyorum" sorusunun cevabı neredeyse her zaman analiz zincirindedir. Elasticsearch metni hem index'lerken hem de ararken analiz eder; ikisi aynı sonucu üretmezse eşleşme olmaz. _analyze API'si, metnin hangi tokenlara bölündüğünü size doğrudan gösterir:

    ES='curl -s --cacert /etc/elasticsearch/certs/http_ca.crt -u elastic'
    
    $ES -X POST 'https://localhost:9200/urunler/_analyze?pretty' \
      -H 'Content-Type: application/json' -d '
    { "analyzer": "turkish", "text": "Kabin Fanları 120mm" }'
    
    # Beklenen tokenlar: kabin, fan, 120mm
    

    Türkçe analyzer'ın "fanları" kelimesini "fan" gövdesine indirdiğini görüyorsunuz; bu yüzden kullanıcı "fan" arattığında "fanları" içeren belge de bulunur. Aynı testi index'teki gerçek alan üzerinde de yapabilirsiniz:

    $ES -X POST 'https://localhost:9200/urunler/_analyze?pretty' \
      -H 'Content-Type: application/json' -d '
    { "field": "baslik", "text": "Kabin Fanları 120mm" }'
    

    Eş anlamlı sözlük, arama kalitesini en çok yükselten ikinci müdahaledir ve kullanıcının kelimeleriyle sizin kelimelerinizin farklı olduğu her yerde işe yarar. Eş anlamlıları arama zamanında uygulamak, index'i yeniden oluşturmadan sözlüğü güncelleyebilmenizi sağlar:

    $ES -X PUT 'https://localhost:9200/urunler/_settings?pretty' \
      -H 'Content-Type: application/json' -d '
    {
      "analysis": {
        "filter": {
          "es_anlamli": {
            "type": "synonym_graph",
            "synonyms": [
              "vds, sanal sunucu, sanal makine",
              "ssd, kati hal diski",
              "kabin, rack"
            ]
          }
        },
        "analyzer": {
          "turkce_arama": {
            "tokenizer": "standard",
            "filter": [ "lowercase", "es_anlamli", "turkish_stem" ]
          }
        }
      }
    }'
    

    Bu ayarı uygulamak için index'i geçici olarak kapatıp açmanız gerekir (_close ve _open); analiz ayarları çalışan bir index üzerinde değiştirilemez.

    Alaka Puanını Yönetmek#

    Elasticsearch varsayılan olarak BM25 algoritmasıyla puan verir: nadir kelimeler daha değerlidir, kısa alandaki eşleşme uzun alandakinden daha güçlüdür. Bu genellikle iyi bir başlangıçtır ama iş kurallarınızı bilmez. Örneğin stokta olan ürünlerin, popüler ürünlerin ya da yeni eklenenlerin üste çıkmasını istiyorsanız puana müdahale etmeniz gerekir.

    En temiz araç function_score'dur. Aşağıdaki örnek, metin alakasını korurken satış adedini ve tazeliği ödüllendirir:

    {
      "query": {
        "function_score": {
          "query": {
            "bool": {
              "must":   [ { "match": { "baslik": "kabin fanı" } } ],
              "filter": [ { "term": { "stokta": true } } ]
            }
          },
          "functions": [
            {
              "field_value_factor": {
                "field": "satisAdedi",
                "modifier": "log1p",
                "factor": 0.6,
                "missing": 0
              }
            },
            {
              "gauss": {
                "eklenme": { "origin": "now", "scale": "90d", "decay": 0.5 }
              }
            }
          ],
          "score_mode": "sum",
          "boost_mode": "multiply"
        }
      }
    }
    

    Üç ayrıntı önemli. modifier: "log1p" olmadan bin satışlı bir ürün, on satışlı bir ürünün yüz katı puan alır ve metin alakası tamamen ezilir; logaritma bu etkiyi yumuşatır. missing: 0 alanı olmayan belgelerin hata vermesini önler. gauss fonksiyonu ise tazelik için idealdir: 90 gün içindeki kayıtlar tam puan alır, sonra yumuşakça düşer.

    Bir sonucun neden o puanı aldığını anlamak için explain kullanın; çıktı uzundur ama hangi terimin ne kadar katkı verdiğini tek tek gösterir:

    $ES -X GET 'https://localhost:9200/urunler/_explain/1002?pretty' \
      -H 'Content-Type: application/json' -d '
    { "query": { "match": { "baslik": "kabin fanı" } } }'
    

    Otomatik Tamamlama ve Yazım Hatası Toleransı#

    Arama kutusunda anlık öneri göstermek, wildcard sorgusuyla yapılmaya çalışıldığında kümeyi dize getirir. Doğru yaklaşım, tamamlama için index zamanında hazırlık yapmaktır. En pratik yol search_as_you_type alan tipidir:

    $ES -X PUT 'https://localhost:9200/urunler/_mapping?pretty' \
      -H 'Content-Type: application/json' -d '
    { "properties": { "oneri": { "type": "search_as_you_type" } } }'
    
    # Kullanıcı "kab" yazdığında
    $ES -X GET 'https://localhost:9200/urunler/_search?pretty' \
      -H 'Content-Type: application/json' -d '
    {
      "size": 5,
      "_source": [ "baslik" ],
      "query": {
        "multi_match": {
          "query": "kab",
          "type": "bool_prefix",
          "fields": [ "oneri", "oneri._2gram", "oneri._3gram" ]
        }
      }
    }'
    

    Yazım hatası toleransı için fuzziness vardır ama dikkatli kullanılmalıdır. Bulanık eşleşme, index'teki terimleri düzenleme mesafesine göre genişletir; bu işlem pahalıdır ve kısa kelimelerde alakasız sonuçlar üretir:

    {
      "query": {
        "match": {
          "baslik": {
            "query": "kabbin fani",
            "fuzziness": "AUTO",
            "prefix_length": 2,
            "max_expansions": 30
          }
        }
      }
    }
    

    prefix_length: 2 ilk iki harfin doğru olmasını şart koşar ve arama uzayını dramatik biçimde daraltır; max_expansions ise genişletme sayısını sınırlar. Bu iki parametre olmadan fuzziness açmak, yoğun bir sitede en sık karşılaştığım Elasticsearch yavaşlama sebeplerinden biridir.

    Derin Sayfalama ve Yanıt Boyutu#

    from ve size ile sayfalama, ilk sayfalarda kusursuz çalışır ve derinlere inildikçe katlanarak pahalılaşır. Sebebi mimaridir: from: 10000, size: 10 isteği, her parçadan 10.010 belge toplayıp koordinatör düğümde birleştirmeyi gerektirir. Elasticsearch bu yüzden varsayılan olarak 10.000'inci sonuçtan sonrasını reddeder.

    # Bu hatayı görüyorsanız derin sayfalama yapıyorsunuz demektir:
    # "Result window is too large, from + size must be less than or equal to: [10000]"
    

    Çözüm, sınırı yükseltmek değildir — bu yalnızca sorunu belleğe taşır. Doğru yöntem search_after ile imleç tabanlı sayfalamadır. Sıralamaya benzersiz bir eşitlik bozucu (genellikle _id ya da bir sayaç) eklemeniz şarttır, aksi hâlde aynı değere sahip kayıtlarda sayfalar arasında kayma olur:

    # İlk sayfa
    {
      "size": 20,
      "sort": [ { "eklenme": "desc" }, { "stokKodu": "asc" } ],
      "query": { "match_all": {} }
    }
    
    # Sonraki sayfa: son belgenin sort değerlerini aynen geri gönderin
    {
      "size": 20,
      "sort": [ { "eklenme": "desc" }, { "stokKodu": "asc" } ],
      "search_after": [ "2026-08-21T11:00:00Z", "PW-C13" ],
      "query": { "match_all": {} }
    }
    

    Yanıt boyutunu küçültmek de ucuz bir kazançtır. Uygulamanız her belgenin tamamına ihtiyaç duymuyorsa _source alanını daraltın; büyük belgelerde ağ trafiği ve serileştirme maliyeti şaşırtıcı derecede yüksektir:

    { "_source": [ "baslik", "fiyat", "stokta" ], "size": 20, "query": { "match_all": {} } }
    

    Toplama sorgularında ise terms toplamasının size parametresini bilinçli verin. Varsayılan 10'dur ve büyük kardinaliteli alanlarda sonuçlar yaklaşık olur; size değerini gereğinden çok büyütmek de bellek tüketir. Benzersiz sayım için cardinality toplaması kullanılır ve bu toplama yaklaşıktır — kesin sayı gerektiren finansal raporlarda buna güvenmeyin.

    Index Tarafı Ayarlar ve Yavaş Sorgu Teşhisi#

    Arama hızının bir bölümü sorguda değil, index tasarımındadır. Üç ayar pratikte fark yaratır.

    Parça boyutu. Her parça ayrı bir Lucene index'idir ve kendi maliyeti vardır. Pratik hedef, parça başına 10 ile 50 GB arasıdır. Birkaç gigabaytlık veri için on parça açmak, sorgunun on ayrı yerde çalışıp birleştirilmesi demektir ve yavaşlatır.

    refresh_interval. Yeni belgeler varsayılan olarak bir saniyede aranabilir hâle gelir. Toplu yükleme sırasında bunu kapatmak yüklemeyi belirgin biçimde hızlandırır:

    # Toplu yükleme öncesi
    $ES -X PUT 'https://localhost:9200/urunler/_settings' -H 'Content-Type: application/json' \
      -d '{ "index": { "refresh_interval": "-1", "number_of_replicas": 0 } }'
    
    # Yükleme bittikten sonra eski hâline döndür
    $ES -X PUT 'https://localhost:9200/urunler/_settings' -H 'Content-Type: application/json' \
      -d '{ "index": { "refresh_interval": "1s", "number_of_replicas": 1 } }'
    

    force merge. Artık yazma almayan index'lerde (örneğin geçmiş aylara ait log index'leri) segmentleri birleştirmek arama hızını artırır. Yalnızca salt okunur index'lerde yapın; yazma alan bir index'te ters etki yaratır:

    $ES -X POST 'https://localhost:9200/loglar-2026-07/_forcemerge?max_num_segments=1'
    

    Yavaş sorguları yakalamak için iki araç var. Birincisi profile bayrağıdır; sorgunun hangi aşamada ne kadar süre harcadığını nanosaniye düzeyinde gösterir:

    $ES -X GET 'https://localhost:9200/urunler/_search?pretty' \
      -H 'Content-Type: application/json' -d '
    { "profile": true, "query": { "match": { "baslik": "kabin fanı" } } }'
    

    İkincisi yavaş sorgu günlüğüdür ve üretimde sürekli açık kalabilir:

    $ES -X PUT 'https://localhost:9200/urunler/_settings' -H 'Content-Type: application/json' -d '
    {
      "index.search.slowlog.threshold.query.warn": "1s",
      "index.search.slowlog.threshold.query.info": "500ms",
      "index.search.slowlog.threshold.fetch.warn": "500ms"
    }'
    
    sudo tail -f /var/log/elasticsearch/*_index_search_slowlog.json
    

    Aynı teşhis disiplinini belge veritabanı tarafında da kurmak isterseniz MongoDB index ve sorgu optimizasyonu yazısındaki yöntem birebir aynı mantığa dayanır: önce ölç, sonra değiştir.

    Arama Kalitesini Ölçmek#

    Alaka ayarlarında en tehlikeli şey, değişikliğin işe yarayıp yaramadığına gözle karar vermektir. Kendi arattığınız üç kelimede sonuç düzeldiği için ağırlığı artırırsınız, farkında olmadan başka on kelimede sonuç bozulur ve bunu ancak kullanıcı şikâyetiyle öğrenirsiniz. Bu yüzden alaka değişikliklerini bir ölçüye bağlamak gerekir.

    Elasticsearch bunun için sıralama değerlendirme (rank evaluation) API'si sunar. Gerçek kullanıcı aramalarından bir örneklem seçer, her arama için doğru kabul ettiğiniz belgeleri işaretler ve sorgu şablonunuzun bu beklentiyi ne kadar karşıladığını sayıya çevirirsiniz:

    $ES -X POST 'https://localhost:9200/urunler/_rank_eval?pretty' \
      -H 'Content-Type: application/json' -d '
    {
      "requests": [
        {
          "id": "kabin-fani",
          "request": { "query": { "match": { "baslik": "kabin fanı" } } },
          "ratings": [
            { "_index": "urunler", "_id": "1002", "rating": 3 },
            { "_index": "urunler", "_id": "1001", "rating": 1 }
          ]
        }
      ],
      "metric": { "precision": { "k": 10, "relevant_rating_threshold": 1 } }
    }'
    

    Dönen metric_score değeri, mevcut sorgu şablonunuzun kalite notudur. Değişiklikten önce ve sonra aynı örneklemle çalıştırıp iki notu karşılaştırırsınız; not düştüyse değişiklik geri alınır. Bu, on beş dakikada kurulan ama aylarca tekrar tekrar işe yarayan bir düzendir.

    Ölçmenin ikinci ayağı, kullanıcıların gerçekte ne arattığını bilmektir. Sonuç dönmeyen aramaları ayrı bir yere kaydedin; bu liste, eş anlamlı sözlüğünüzü büyütmek için elinizdeki en değerli kaynaktır. "Sıfır sonuçlu arama" oranını haftalık takip etmek, tek bir teknik ayardan çok daha fazla iyileştirme fırsatı gösterir.

    Son olarak sonuç sayısına da bakın. Bir arama on binlerce belge döndürüyorsa filtre eksik demektir; üç belge döndürüyorsa analiz zinciriniz fazla agresif olabilir. İki uç da kullanıcıyı aynı yere götürür: aradığını bulamadığı bir sayfaya.

    Sıkça Sorulan Sorular#

    Elasticsearch sorgum neden yavaş#

    En sık üç sebep var. Birincisi, puanlanması gerekmeyen koşulların must içinde durması; bunları filter bağlamına taşımak önbellekten yararlanmayı sağlar. İkincisi derin sayfalama; from değeri binleri geçtiğinde her parçadan devasa sonuç kümesi toplanır. Üçüncüsü wildcard, sınırsız fuzziness ya da baştan sabitlenmemiş düzenli ifade gibi terim genişletmesi yapan sorgular. profile: true ile çalıştırıp hangi aşamanın süreyi yediğini görün.

    term sorgusu neden hiç sonuç döndürmüyor#

    Neredeyse her zaman alan tipiyle sorgu tipinin uyuşmamasından kaynaklanır. term sorgusu verdiğiniz değeri analiz etmez, ama text alanlar index'lenirken analiz edilip küçük harfli kelimelere bölünür. Sonuçta index'te "kabin" ve "fan" tokenları varken siz "Kabin Fanı" ararsınız ve eşleşme olmaz. Tam eşleşme için alanı keyword yapın ya da çok alanlı tanımdaki .keyword alt alanını kullanın.

    10000'inci sonuçtan sonrasını nasıl gösteririm#

    index.max_result_window sınırını yükseltmek yerine search_after kullanın. Sıralamaya benzersiz bir eşitlik bozucu alan ekleyip her sayfada son belgenin sort değerlerini bir sonraki isteğe taşırsınız. Bu yöntem sabit maliyetlidir, yani binlerce sayfa ilerlerken yavaşlamaz. Sınırı büyütmek ise yalnızca sorunu koordinatör düğümün belleğine taşır ve yoğun anlarda kümeyi riske atar.

    Arama sonuçlarının sıralamasını nasıl değiştiririm#

    İki katmanınız var. Basit ihtiyaçlar için alan ağırlıkları yeterlidir: "fields": ["baslik^3", "aciklama"] yazarak başlıktaki eşleşmeyi üç kat değerli yaparsınız. Daha karmaşık iş kuralları için function_score kullanın; satış adedi, stok durumu ya da tazelik gibi sinyalleri metin alakasıyla çarparak birleştirebilirsiniz. Sayısal sinyalleri mutlaka logaritmik bir dönüşümle yumuşatın, yoksa metin alakası tamamen ezilir.

    Türkçe arama sonuçlarını nasıl iyileştiririm#

    Üç adım en çok fark yaratır. Birincisi yerleşik turkish analyzer'ı kullanmak; ekleri ayıklar ve "sunucular" aramasının "sunucu" belgelerini bulmasını sağlar. İkincisi eş anlamlı sözlük eklemek; kullanıcının kelimeleriyle sizin kelimeleriniz genellikle aynı değildir. Üçüncüsü alan ağırlıklarını ayarlamak. _analyze API'siyle her değişikliğin token çıktısını doğrulayın, tahminle ilerlemeyin.

    Index'i yeniden oluşturmadan analyzer değiştirebilir miyim#

    Arama zamanı analyzer'ını değiştirmek mümkündür ve eş anlamlı sözlüğü bu yüzden arama tarafında tutmak iyi bir alışkanlıktır. Ancak index zamanı analyzer'ını değiştirirseniz, mevcut belgeler eski token'larla index'lenmiş olarak kalır ve yeni sorgularla eşleşmez. Bu durumda yeni bir index oluşturup _reindex ile veriyi taşımanız gerekir; takma ad kullanıyorsanız geçiş kullanıcıya hiç yansımaz.

    Kapanış#

    Elasticsearch'te optimizasyon, ayar dosyasında değil sorgunun kendisinde başlar. Aklınızda kalması gereken dört alışkanlık: puanı etkilemeyen her koşulu filter bağlamına taşıyın, alan tipiyle sorgu tipini eşleştirin (text için match, keyword için term), derin sayfalamayı search_after ile değiştirin ve her değişikliği profile: true ya da yavaş sorgu günlüğüyle ölçün. Alaka tarafında ise önce _analyze çıktısına bakın; "bulunamıyor" şikâyetlerinin çoğu puanlama değil, tokenlaşma sorunudur.

    Bu ölçümlerin anlamlı olması için sunucunuzun kaynaklarının size ait ve öngörülebilir olması gerekir. Tam kaynak garantili VDS ve bulut sunucu paketlerimiz Elasticsearch için uygun bir zemin sunar; yüksek hacimli index'ler ve yoğun toplama sorguları için dedicated sunucu tarafına bakabilirsiniz. Kurulum, ayar ve izleme yükünü devretmek isterseniz sunucu yönetimi hizmetimiz bu işi üstlenir.

    ElasticsearchAramaPerformans

    Uygulamaya geçmeye hazır mısınız?

    NVMe SSD, ücretsiz SSL ve %99.9 uptime garantisiyle Clou.TR hosting ve sunucu çözümleriyle projenizi hayata geçirin.