← Kursa Dön
📄 Text · 30 min

Query Optimizasyonu — Profiling ve Slow Log

Giriş — Filter Cache, Profiling, Slow Log ve Execution Plan

Bir restoranda düşün: Garson siparişi alıp mutfağa taşıyor, aşçı pişiriyor, garson masaya getiriyor. Yemek 40 dakika sürüyorsa, sorun garsonda mı, aşçıda mı, serviste mi? Bunu anlamak için her adımı ayrı ayrı ölçmen gerek. "Yemek yavaş geldi" demek yetmez — tam olarak nerede yavaş olduğunu bilmen gerek.

Elasticsearch query optimizasyonu aynı mantık. Bir sorgu yavaşsa, nedeni onlarca farklı şey olabilir: kötü yapılandırılmış query, cache'lenmeyen filter, büyük result set, aşırı shard taraması... Sorunu bulmak için Elasticsearch'ün sunduğu araçları — Profile API, Slow Log, Filter Cache mekanizmasını ve Search Profiler'ı — ustaca kullanman gerek.


1. Query Context vs Filter Context — Temel Fark

Bu konuyu daha önce gördük ama optimizasyon açısından tekrar vurgulayalım çünkü performans farkı devasa.

Query Context

  • Soru: "Bu document ne kadar iyi eşleşiyor?" (relevance scoring)

  • Sonuç: _score hesaplanır

  • Cache'lenmez

Filter Context

  • Soru: "Bu document eşleşiyor mu, evet veya hayır?" (boolean)

  • Sonuç: _score hesaplanmaz → daha hızlı

  • Cache'lenir → tekrarlayan sorgularda devasa hız kazanımı

// ❌ YAVAŞ — Her şey query context'te
GET products/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "name": "laptop" } },
        { "range": { "price": { "gte": 5000, "lte": 15000 } } },
        { "term": { "category": "electronics" } },
        { "term": { "in_stock": true } }
      ]
    }
  }
}

// ✅ HIZLI — Scoring gerektirmeyen kısımlar filter'da
GET products/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "name": "laptop" } }
      ],
      "filter": [
        { "range": { "price": { "gte": 5000, "lte": 15000 } } },
        { "term": { "category": "electronics" } },
        { "term": { "in_stock": true } }
      ]
    }
  }
}

İkinci sorguda range, category, in_stock filtreleri:

  1. Score hesaplamaz → CPU tasarrufu

  2. Bitset olarak cache'lenir → aynı filtre tekrar geldiğinde disk'e bile gitmez

Filter Cache Nasıl Çalışır?

Elasticsearch, filter sonuçlarını node-level request cache ve shard-level query cache olarak saklar.

Node Request Cache:

# Cache durumunu kontrol et
GET _nodes/stats/indices/request_cache

# Belirli bir index için
GET _cat/indices/products?v&h=index,request_cache.memory_size,request_cache.hit_count,request_cache.miss_count

Cache'i temizleme (test ortamında):

# Tüm cache'leri temizle
POST _cache/clear

# Belirli index
POST products/_cache/clear

# Sadece request cache
POST _cache/clear?request=true

💡 İpucu: Filter cache, aynı sorgu aynı shard'a tekrar geldiğinde otomatik çalışır. Siz bir şey yapmanız gerekmez — sadece filter context kullanmayı unutmayın.


2. Profile API — Sorgunun Röntgenini Çekme

Profile API, bir sorgunun her adımında ne kadar zaman harcandığını gösterir. Yavaş sorguların teşhisinde en güçlü araç.

Temel Kullanım

GET products/_search
{
  "profile": true,
  "query": {
    "bool": {
      "must": [
        { "match": { "name": "gaming laptop" } }
      ],
      "filter": [
        { "range": { "price": { "gte": 5000, "lte": 20000 } } },
        { "term": { "brand": "asus" } }
      ]
    }
  }
}

Profile Çıktısını Okuma

{
  "profile": {
    "shards": [
      {
        "id": "[node-1][products][0]",
        "searches": [
          {
            "query": [
              {
                "type": "BooleanQuery",
                "description": "+name:gaming +name:laptop #(price:[5000 TO 20000] #brand:asus)",
                "time_in_nanos": 1250000,
                "breakdown": {
                  "score": 450000,
                  "build_scorer_count": 2,
                  "build_scorer": 320000,
                  "create_weight": 80000,
                  "create_weight_count": 1,
                  "advance": 150000,
                  "advance_count": 500,
                  "match": 200000,
                  "match_count": 500,
                  "next_doc": 50000,
                  "next_doc_count": 500
                },
                "children": [
                  {
                    "type": "TermQuery",
                    "description": "name:gaming",
                    "time_in_nanos": 380000,
                    "breakdown": { "..." : "..." }
                  },
                  {
                    "type": "TermQuery",
                    "description": "name:laptop",
                    "time_in_nanos": 420000,
                    "breakdown": { "..." : "..." }
                  }
                ]
              }
            ],
            "collector": [
              {
                "name": "SimpleTopScoreDocCollector",
                "reason": "search_top_hits",
                "time_in_nanos": 200000
              }
            ]
          }
        ],
        "aggregations": []
      }
    ]
  }
}

Breakdown Metrikleri

MetrikAçıklama
create_weightSorgu ağacını hazırlama süresi
build_scorerScorer objesini oluşturma
next_docSonraki eşleşen document'ı bulma
advanceBelirli bir document'a atlama
matchİki-fazlı eşleştirme (phrase query gibi)
scoreRelevance score hesaplama

⚠️ Dikkat: profile: true sorguyu yavaşlatır (profiling overhead). Production'da sürekli açık bırakmayın — sadece debug amaçlı kullanın.

Aggregation Profiling

GET sales/_search
{
  "profile": true,
  "size": 0,
  "aggs": {
    "monthly_sales": {
      "date_histogram": {
        "field": "date",
        "calendar_interval": "month"
      },
      "aggs": {
        "total_revenue": {
          "sum": { "field": "revenue" }
        }
      }
    }
  }
}

Aggregation profili, hangi agg'in ne kadar sürdüğünü ayrıntılı gösterir. Özellikle nested aggregation'larda darboğaz tespiti için kritik.


3. Slow Log — Yavaş Sorguları Yakalama

Slow Log, belirlediğiniz eşik süresini aşan sorguları otomatik loglar. Production'da "hangi sorgular yavaş?" sorusunun cevabı.

Slow Log Ayarlama

PUT products/_settings
{
  "index.search.slowlog.threshold.query.warn": "5s",
  "index.search.slowlog.threshold.query.info": "2s",
  "index.search.slowlog.threshold.query.debug": "1s",
  "index.search.slowlog.threshold.query.trace": "500ms",

  "index.search.slowlog.threshold.fetch.warn": "1s",
  "index.search.slowlog.threshold.fetch.info": "500ms",
  "index.search.slowlog.threshold.fetch.debug": "200ms",
  "index.search.slowlog.threshold.fetch.trace": "100ms",

  "index.search.slowlog.level": "info"
}

Query vs Fetch fazı:

  • Query: Eşleşen document'ları bulma (scoring, filtering)

  • Fetch: Bulunan document'ların _source'unu getirme

Indexing Slow Log

Sadece arama değil, yazma operasyonları için de slow log ayarlayabilirsiniz:

PUT products/_settings
{
  "index.indexing.slowlog.threshold.index.warn": "10s",
  "index.indexing.slowlog.threshold.index.info": "5s",
  "index.indexing.slowlog.threshold.index.debug": "2s",
  "index.indexing.slowlog.threshold.index.trace": "500ms",
  "index.indexing.slowlog.source": "1000"
}

index.indexing.slowlog.source: Log'a yazılacak source'un max karakter sayısı. 0 kapalı, true tamamı.

Slow Log Çıktısını Okuma

Log dosyası: <es-path>/logs/<cluster-name>_index_search_slowlog.log

[2024-01-15T14:30:45,123][WARN ][index.search.slowlog.query] [node-1]
  [products/abc123] took[5.2s], took_millis[5234],
  total_hits[15234], types[], stats[],
  search_type[QUERY_THEN_FETCH],
  total_shards[5], source[{"query":{"match":{"description":"gaming laptop..."}},"size":100}]

Bu log'dan anlayacaklarınız:

  • Sorgu 5.2 saniye sürmüş

  • 15.234 hit bulmuş

  • 5 shard taramış

  • Sorgunun kendisi source alanında

Cluster-Level Slow Log (Tüm index'ler için)

PUT _cluster/settings
{
  "persistent": {
    "cluster.search.slow_log.threshold.query.warn": "5s",
    "cluster.search.slow_log.threshold.query.info": "2s"
  }
}

4. Query Optimizasyon Teknikleri

4.1. Gereksiz Field'ları Getirme

// ❌ YAVAŞ — Tüm _source getiriliyor (büyük document'larda ağır)
GET products/_search
{
  "query": { "match": { "name": "laptop" } }
}

// ✅ HIZLI — Sadece gereken field'lar
GET products/_search
{
  "_source": ["name", "price", "brand"],
  "query": { "match": { "name": "laptop" } }
}

// ✅ DAHA DA HIZLI — stored_fields veya docvalue_fields
GET products/_search
{
  "_source": false,
  "docvalue_fields": ["price", "brand", "created_at"],
  "query": { "match": { "name": "laptop" } }
}

docvalue_fields, keyword ve numeric field'lar için disk'ten columnar formatta okur — _source parse etmekten hızlıdır.

4.2. Size ve Pagination Optimizasyonu

// ❌ YAVAŞ — Deep pagination
GET products/_search
{
  "from": 10000,
  "size": 10,
  "query": { "match_all": {} }
}
// 10.010 document sıralanıp ilk 10.000'i atılır!

// ✅ HIZLI — search_after kullan
GET products/_search
{
  "size": 10,
  "query": { "match_all": {} },
  "sort": [
    { "created_at": "desc" },
    { "_id": "asc" }
  ],
  "search_after": ["2024-01-15T10:00:00Z", "abc123"]
}

4.3. Shard Routing ile Hedefli Arama

// ❌ Tüm shard'lar taranıyor
GET logs/_search
{
  "query": {
    "bool": {
      "filter": [
        { "term": { "tenant_id": "acme-corp" } }
      ]
    }
  }
}

// ✅ Sadece ilgili shard taranıyor
GET logs/_search?routing=acme-corp
{
  "query": {
    "bool": {
      "filter": [
        { "term": { "tenant_id": "acme-corp" } }
      ]
    }
  }
}

4.4. Preference ile Cache Optimizasyonu

// Her seferinde aynı shard kopyasına git → cache hit oranı artar
GET products/_search?preference=_prefer_local
{
  "query": { "match": { "name": "laptop" } }
}

// Kullanıcı bazlı preference (session affinity)
GET products/_search?preference=user_12345
{
  "query": { "match": { "name": "laptop" } }
}

preference parametresi, aynı sorguyu hep aynı shard kopyasına yönlendirir. Bu, o shard'ın OS page cache'ini sıcak tutar.

4.5. Wildcard ve Regexp Optimizasyonu

// ❌ ÇOK YAVAŞ — Leading wildcard (tüm term'leri tarar)
GET products/_search
{
  "query": {
    "wildcard": { "name": "*laptop*" }
  }
}

// ✅ DAHA İYİ — Prefix wildcard (index'teki sıralı yapıyı kullanır)
GET products/_search
{
  "query": {
    "wildcard": { "name": "laptop*" }
  }
}

// ✅ EN İYİ — match query (analyzer kullanır)
GET products/_search
{
  "query": {
    "match": { "name": "laptop" }
  }
}

⚠️ Dikkat: *laptop* şeklindeki wildcard'lar, index'teki tüm unique term'leri tarar. Milyonlarca unique term varsa saniyeler sürebilir.

4.6. Scripting Performansı

// ❌ YAVAŞ — Her document için script çalışır
GET products/_search
{
  "query": {
    "script_score": {
      "query": { "match_all": {} },
      "script": {
        "source": "doc['price'].value * doc['discount_rate'].value"
      }
    }
  }
}

// ✅ DAHA İYİ — Önceden hesaplanmış field kullan
// Index-time'da discounted_price field'ı hesapla
PUT products/_mapping
{
  "properties": {
    "discounted_price": { "type": "float" }
  }
}

// Sonra basit range query yeterli
GET products/_search
{
  "query": {
    "range": {
      "discounted_price": { "gte": 1000, "lte": 5000 }
    }
  }
}

4.7. Aggregation Optimizasyonu

// ❌ YAVAŞ — Global aggregation (tüm document'lar)
GET logs/_search
{
  "size": 0,
  "aggs": {
    "status_codes": {
      "terms": {
        "field": "status_code",
        "size": 100
      }
    }
  }
}

// ✅ HIZLI — Filtreli aggregation
GET logs/_search
{
  "size": 0,
  "query": {
    "bool": {
      "filter": [
        { "range": { "@timestamp": { "gte": "now-1h" } } }
      ]
    }
  },
  "aggs": {
    "status_codes": {
      "terms": {
        "field": "status_code",
        "size": 10
      }
    }
  }
}

Aggregation'lar her zaman query sonuçları üzerinde çalışır. Önce filtre ile sonuç setini küçültmek, aggregation'ı hızlandırır.


5. Execution Plan — Sorgunun Arka Planı

Search Type

Elasticsearch iki aşamalı arama yapar:

  1. Query Phase: Her shard'da eşleşen document ID'leri ve score'lar bulunur

  2. Fetch Phase: En iyi sonuçların _source'u getirilir

// Varsayılan: query_then_fetch
GET products/_search?search_type=query_then_fetch
{
  "query": { "match": { "name": "laptop" } }
}

// DFS query then fetch: Daha doğru scoring (distributed term frequencies)
GET products/_search?search_type=dfs_query_then_fetch
{
  "query": { "match": { "name": "laptop" } }
}

dfs_query_then_fetch ne zaman kullanılır? Küçük veri setlerinde term frequency dağılımı shard'lar arasında dengesiz olabilir. Bu durumda scoring tutarsız olur. DFS, önce tüm shard'lardan term frequency toplar, sonra arama yapar.

Adaptive Replica Selection

Elasticsearch 7.x+ varsayılan olarak sorguları en hızlı yanıt verecek shard kopyasına yönlendirir:

// Bu özellik varsayılan olarak açık
PUT _cluster/settings
{
  "persistent": {
    "cluster.routing.use_adaptive_replica_selection": true
  }
}

6. Java ile Query Profiling

import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Profile;
import co.elastic.clients.elasticsearch.core.search.ShardProfile;
import co.elastic.clients.elasticsearch.core.search.QueryProfile;

// Profile'lı arama
SearchResponse<Product> response = client.search(s -> s
    .index("products")
    .profile(true)
    .query(q -> q
        .bool(b -> b
            .must(m -> m
                .match(mt -> mt
                    .field("name")
                    .query("gaming laptop")
                )
            )
            .filter(f -> f
                .range(r -> r
                    .field("price")
                    .gte(JsonData.of(5000))
                    .lte(JsonData.of(20000))
                )
            )
        )
    ),
    Product.class
);

// Profile sonuçlarını oku
Profile profile = response.profile();
if (profile != null) {
    for (ShardProfile shard : profile.shards()) {
        System.out.println("Shard: " + shard.id());
        for (var search : shard.searches()) {
            for (QueryProfile query : search.query()) {
                System.out.println("  Query type: " + query.type());
                System.out.println("  Time: " + query.timeInNanos() / 1_000_000.0 + "ms");
                System.out.println("  Description: " + query.description());
            }
        }
    }
}

Slow Query Logger (Java Client)

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.Duration;
import java.time.Instant;

public class ElasticsearchQueryLogger {
    private static final Logger log = LoggerFactory.getLogger(ElasticsearchQueryLogger.class);
    private static final long SLOW_QUERY_THRESHOLD_MS = 1000;

    private final ElasticsearchClient client;

    public ElasticsearchQueryLogger(ElasticsearchClient client) {
        this.client = client;
    }

    public <T> SearchResponse<T> searchWithLogging(
            SearchRequest request, Class<T> clazz) throws Exception {

        Instant start = Instant.now();

        SearchResponse<T> response = client.search(
            SearchRequest.of(b -> b
                .index(request.index())
                .query(request.query())
                .size(request.size())
            ), clazz);

        long elapsed = Duration.between(start, Instant.now()).toMillis();

        if (elapsed > SLOW_QUERY_THRESHOLD_MS) {
            log.warn("SLOW QUERY detected! Index: {}, Duration: {}ms, " +
                     "Hits: {}, Shards: {}/{}",
                request.index(),
                elapsed,
                response.hits().total().value(),
                response.shards().successful(),
                response.shards().total()
            );
        }

        return response;
    }
}

7. Request Cache ve Shard Query Cache

Request Cache

Index değişmediyse, aynı sorgunun sonucu cache'ten döner.

// Request cache'i index bazında kontrol et
GET _cat/indices/products?v&h=index,rc.memory_size,rc.evictions

// Request cache'i açma/kapama
PUT products/_settings
{
  "index.requests.cache.enable": true
}

// Tek bir sorguda cache'i devre dışı bırak
GET products/_search?request_cache=false
{
  "size": 0,
  "aggs": {
    "avg_price": {
      "avg": { "field": "price" }
    }
  }
}

Request cache kuralları:

  • size: 0 olan sorgularda (sadece aggregation) varsayılan açık

  • Document döndüren sorgularda varsayılan kapalı

  • Index refresh olunca cache invalidate olur

Shard-Level Query Cache (Node Query Cache)

Filter context'teki sorguların bitset sonuçlarını cache'ler.

# Node query cache istatistikleri
GET _nodes/stats/indices/query_cache

# Çıktıda önemli metrikler:
# - memory_size_in_bytes: Cache boyutu
# - hit_count: Cache'ten dönen sorgu sayısı
# - miss_count: Cache'te bulunamayan
# - evictions: Cache'ten çıkarılan entry sayısı

Cache boyutu varsayılan: heap'in %10'u. Değiştirmek için elasticsearch.yml:

indices.queries.cache.size: 15%

8. Bütünleşik Örnek: E-Commerce Arama Optimizasyonu

Bir e-ticaret sitesinde ürün arama sorgusunu adım adım optimize edelim.

Başlangıç — Optimize Edilmemiş Sorgu

// Orijinal sorgu — 850ms süre, kabul edilemez
GET products/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "name": "samsung galaxy" } },
        { "term": { "category": "phones" } },
        { "term": { "in_stock": true } },
        { "range": { "price": { "gte": 5000, "lte": 25000 } } },
        { "term": { "seller_verified": true } }
      ]
    }
  },
  "sort": [
    { "_score": "desc" }
  ],
  "from": 0,
  "size": 50
}

Adım 1: Filter Context Kullanımı

// must → filter (scoring gerekmeyenler)
// 850ms → 320ms
GET products/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "name": "samsung galaxy" } }
      ],
      "filter": [
        { "term": { "category": "phones" } },
        { "term": { "in_stock": true } },
        { "range": { "price": { "gte": 5000, "lte": 25000 } } },
        { "term": { "seller_verified": true } }
      ]
    }
  },
  "from": 0,
  "size": 50
}

Adım 2: Source Filtering

// Sadece gerekli field'ları getir
// 320ms → 280ms
GET products/_search
{
  "_source": ["name", "price", "image_url", "rating", "seller_name"],
  "query": {
    "bool": {
      "must": [
        { "match": { "name": "samsung galaxy" } }
      ],
      "filter": [
        { "term": { "category": "phones" } },
        { "term": { "in_stock": true } },
        { "range": { "price": { "gte": 5000, "lte": 25000 } } },
        { "term": { "seller_verified": true } }
      ]
    }
  },
  "from": 0,
  "size": 20
}

Adım 3: Size Küçültme + Routing

// Routing ekle (multi-tenant senaryo)
// 280ms → 95ms
GET products/_search?routing=marketplace_tr&preference=_local
{
  "_source": ["name", "price", "image_url", "rating", "seller_name"],
  "query": {
    "bool": {
      "must": [
        { "match": { "name": "samsung galaxy" } }
      ],
      "filter": [
        { "term": { "category": "phones" } },
        { "term": { "in_stock": true } },
        { "range": { "price": { "gte": 5000, "lte": 25000 } } },
        { "term": { "seller_verified": true } }
      ]
    }
  },
  "from": 0,
  "size": 20
}

Adım 4: Profile ile Doğrulama

GET products/_search?routing=marketplace_tr
{
  "profile": true,
  "_source": ["name", "price"],
  "query": {
    "bool": {
      "must": [
        { "match": { "name": "samsung galaxy" } }
      ],
      "filter": [
        { "term": { "category": "phones" } },
        { "term": { "in_stock": true } }
      ]
    }
  },
  "size": 20
}

Sonuç: 850ms → 95ms. ~9x hızlanma, sadece query yapısını değiştirerek.


9. Best Practices

✅ Yap

KonuÖneri
Score gerekmiyorsafilter context kullan
Büyük document'lar_source filtering veya docvalue_fields
Deep paginationsearch_after kullan, from/size değil
Slow logProduction'da her zaman açık olsun
WildcardLeading wildcard (*text) kullanma
AggregationQuery ile sonuç setini küçült, sonra aggregate et
ScriptMümkünse index-time'da hesapla

❌ Yapma

KonuNeden
profile: true production'daOverhead ekler, debug amaçlı
from: 1000010K document sıralanıp atılır
match_all + büyük sizeTüm index'i tarar ve hafızaya alır
Script-based sortingHer document için script çalışır
_all field aramaDeprecated ve yavaş

10. Yaygın Hatalar ve Çözümleri

Hata 1: Filter Kullanmayı Unutmak

// Sorun: Tüm clause'lar must'ta → cache yok, score hesaplanıyor
// Çözüm: Score gerekmeyen clause'ları filter'a taşı

// ❌
"must": [
  { "term": { "status": "active" } },
  { "match": { "title": "elasticsearch" } }
]

// ✅
"must": [
  { "match": { "title": "elasticsearch" } }
],
"filter": [
  { "term": { "status": "active" } }
]

Hata 2: Slow Log Eşiklerini Çok Yüksek Tutmak

// ❌ Sadece 10s üstü loglanıyor — çoğu yavaş sorgu kaçıyor
"index.search.slowlog.threshold.query.warn": "10s"

// ✅ Daha agresif eşikler
"index.search.slowlog.threshold.query.warn": "3s",
"index.search.slowlog.threshold.query.info": "1s",
"index.search.slowlog.threshold.query.debug": "500ms"

Hata 3: Cache Invalidation Beklememek

// Sorun: Filter cache var ama her seferinde miss oluyor
// Neden: Index çok sık refresh oluyor (varsayılan 1s)
// Çözüm: Yazma yoğun index'lerde refresh interval'ı artır

PUT heavy-write-index/_settings
{
  "index.refresh_interval": "30s"
}

Hata 4: Gereksiz Highlight

// ❌ Tüm field'larda highlight — yavaş
GET articles/_search
{
  "query": { "match": { "content": "elasticsearch" } },
  "highlight": {
    "fields": {
      "*": {}
    }
  }
}

// ✅ Sadece gerekli field'da
GET articles/_search
{
  "query": { "match": { "content": "elasticsearch" } },
  "highlight": {
    "fields": {
      "content": {
        "fragment_size": 150,
        "number_of_fragments": 3
      }
    }
  }
}

Hata 5: Track Total Hits

// Elasticsearch 7+ varsayılan olarak 10.000'den sonra total hit saymaz
// Bu bir optimizasyon — zorla kapatmayın

// ❌ Tüm hit'leri saydırmak (yavaşlatır)
GET products/_search
{
  "track_total_hits": true,
  "query": { "match_all": {} }
}

// ✅ Varsayılanı koru veya kapalı tut
GET products/_search
{
  "track_total_hits": false,
  "query": { "match": { "name": "laptop" } }
}

11. Monitoring: Query Performance Dashboard

Production'da sorgu performansını sürekli izlemek için temel metrikler:

# Arama performans metrikleri
GET _nodes/stats/indices/search

# Önemli alanlar:
# - search.query_total: Toplam sorgu sayısı
# - search.query_time_in_millis: Toplam sorgu süresi
# - search.query_current: Şu an çalışan sorgu sayısı
# - search.fetch_total: Toplam fetch sayısı
# - search.fetch_time_in_millis: Toplam fetch süresi

# Index bazında
GET _cat/indices?v&h=index,search.query_total,search.query_time

Ortalama sorgu süresi formülü:

Avg Query Time = search.query_time_in_millis / search.query_total

Bu değer 100ms'in altındaysa genel olarak iyi durumdasınız. 500ms üzeriyse optimizasyon gerekir.


Özet

  1. Filter context kullanın — score gerekmiyorsa filter bloğuna koyun. Cache'lenir ve daha hızlıdır.

  2. Profile API yavaş sorguların teşhisinde en güçlü araç — her query bileşeninin süresini gösterir, ama production'da sürekli açık bırakmayın.

  3. Slow Log production'da her zaman aktif olmalı — yavaş sorguları otomatik yakalar, sonradan analiz imkanı verir.

  4. Source filtering ile sadece gereken field'ları getirin — özellikle büyük document'larda belirgin fark yaratır.

  5. Deep pagination yerine search_after kullanın — from: 10000 demek 10K document'ı sıralayıp atmak demektir.

  6. Wildcard'ın başına * koymayın — leading wildcard tüm term'leri tarar. Match query veya prefix wildcard tercih edin.