正在加载,请稍候…

Elasticsearch in Production: Indexing, Search Relevance, and Cluster Operations

Build production-grade Elasticsearch: ILM for time-series, mapping design, BM25 and vector search relevance tuning, bulk indexing, and cluster health management.

Elasticsearch in Production: A Practical Operations Guide

Elasticsearch powers search at GitHub, Wikipedia, and Shopify. Production operation requires understanding characteristics that differ significantly from traditional databases.

Index Mapping Design

Unlike databases, changing Elasticsearch mappings on existing indices requires full reindexing. Get it right upfront:

PUT /products
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1,
    "index.refresh_interval": "30s",
    "analysis": {
      "analyzer": {
        "product_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": ["lowercase", "stop", "snowball", "synonym_filter"]
        }
      },
      "filter": {
        "synonym_filter": {
          "type": "synonym",
          "synonyms": ["iphone,apple phone => phone", "laptop,notebook,computer"]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "product_id": { "type": "keyword" },
      "name": {
        "type": "text",
        "analyzer": "product_analyzer",
        "fields": {
          "keyword": { "type": "keyword" },
          "completion": { "type": "completion" }
        }
      },
      "price": { "type": "scaled_float", "scaling_factor": 100 },
      "category": { "type": "keyword" },
      "in_stock": { "type": "boolean" },
      "embedding": {
        "type": "dense_vector",
        "dims": 768,
        "index": true,
        "similarity": "cosine"
      }
    }
  }
}

Index Lifecycle Management (ILM)

Automate rollover, optimization, and deletion for time-series indices:

PUT _ilm/policy/logs-policy
{
  "policy": {
    "phases": {
      "hot": {
        "actions": {
          "rollover": { "max_size": "50gb", "max_age": "1d", "max_docs": 100000000 }
        }
      },
      "warm": {
        "min_age": "3d",
        "actions": {
          "shrink": { "number_of_shards": 1 },
          "forcemerge": { "max_num_segments": 1 }
        }
      },
      "cold": { "min_age": "30d", "actions": { "freeze": {} } },
      "delete": { "min_age": "90d", "actions": { "delete": {} } }
    }
  }
}

Search Relevance Tuning

GET /products/_search
{
  "query": {
    "function_score": {
      "query": {
        "bool": {
          "must": [{
            "multi_match": {
              "query": "wireless headphones",
              "fields": ["name^3", "description^1", "tags^2"],
              "type": "best_fields",
              "fuzziness": "AUTO",
              "minimum_should_match": "75%"
            }
          }],
          "filter": [
            { "term": { "in_stock": true } },
            { "range": { "price": { "gte": 20, "lte": 500 } } }
          ]
        }
      },
      "functions": [
        { "gauss": { "rating": { "origin": 5.0, "scale": 2.0, "decay": 0.5 } }, "weight": 2 },
        { "gauss": { "created_at": { "origin": "now", "scale": "30d", "decay": 0.5 } }, "weight": 0.5 }
      ],
      "score_mode": "multiply"
    }
  }
}

Hybrid Search: BM25 + Vector Embeddings

from elasticsearch import Elasticsearch
from sentence_transformers import SentenceTransformer

es = Elasticsearch("https://localhost:9200")
model = SentenceTransformer('all-MiniLM-L6-v2')

def semantic_search(query: str, size: int = 10):
    embedding = model.encode(query).tolist()
    return es.search(
        index='products',
        body={
            "query": {
                "bool": {
                    "should": [{
                        "multi_match": {
                            "query": query,
                            "fields": ["name^2", "description"],
                            "boost": 0.7
                        }
                    }]
                }
            },
            "knn": {
                "field": "embedding",
                "query_vector": embedding,
                "k": 10,
                "num_candidates": 100,
                "boost": 0.3
            },
            "size": size
        }
    )["hits"]["hits"]

Bulk Indexing

from elasticsearch.helpers import parallel_bulk

def generate_actions(products):
    for p in products:
        yield {"_index": "products", "_id": p["product_id"], "_source": p}

for ok, action in parallel_bulk(
    es, generate_actions(products),
    thread_count=4, chunk_size=1000,
    max_chunk_bytes=10 * 1024 * 1024
):
    if not ok:
        handle_error(action)

Cluster Health Monitoring

GET /_cluster/health
# green: all good | yellow: replica unassigned | red: data loss risk

GET /_cat/nodes?v&h=name,heapPercent,cpu,load_1m
# heapPercent > 85%: GC pressure warning

# Fix unassigned replicas (single-node dev)
PUT /my_index/_settings
{ "number_of_replicas": 0 }

# Explain allocation problems
GET /_cluster/allocation/explain

# Circuit breaker (prevents OOM)
PUT /_cluster/settings
{
  "persistent": { "indices.breaker.total.limit": "70%" }
}

Slow Log

PUT /my_index/_settings
{
  "index.search.slowlog.threshold.query.warn": "5s",
  "index.search.slowlog.threshold.query.info": "1s",
  "index.search.slowlog.threshold.fetch.warn": "500ms"
}

Forcemerge for Read-Only Indices

POST /my_old_index/_forcemerge?max_num_segments=1
# Reduces segment count, reclaims disk space for cold indices

Elasticsearch clusters that run smoothly are those where teams define mappings upfront, implement ILM for time-series, tune relevance for their domain, and monitor heap usage proactively.