Guida alle Performance di Indicizzazione di Elasticsearch: Best Practice Svelate

Migliora l'indicizzazione di Elasticsearch con richieste bulk, ottimizzazione di refresh e repliche, scelte di mapping, controlli hardware e pianificazione degli shard.

Guida alle Performance di Indicizzazione di Elasticsearch: Best Practice Svelate

Le performance di indicizzazione di Elasticsearch diventano visibili quando la pipeline di ingest inizia a intasarsi, le richieste bulk vengono rifiutate o le ricerche rallentano durante scritture pesanti. La soluzione raramente è un'unica impostazione magica; è necessario ottimizzare insieme dimensione delle richieste, comportamento del refresh, mapping, layout degli shard e hardware.

Questa guida si concentra su controlli pratici delle performance di indicizzazione di Elasticsearch che puoi applicare prima e durante un grande lavoro di ingest. Usali con le metriche del tuo cluster, perché dimensione dei documenti, analizzatori, storage e numero di repliche possono cambiare il risultato.

Comprendere il Processo di Indicizzazione

Prima di immergerci nell'ottimizzazione, è essenziale capire come Elasticsearch gestisce l'indicizzazione. Quando un documento viene indicizzato, Elasticsearch esegue diverse operazioni: analisi del documento, analisi dei campi (tokenizzazione, stemming, ecc.) e quindi memorizzazione dell'indice invertito e di altre strutture dati. Queste operazioni, specialmente l'analisi e l'I/O su disco, sono intensive per CPU e I/O. In un ambiente distribuito, queste operazioni sono gestite da singoli nodi, rendendo critiche la configurazione a livello di cluster e le risorse dei nodi.

Fattori Chiave che Influenzano la Velocità di Indicizzazione

Diversi fattori possono influenzare significativamente la velocità con cui Elasticsearch può indicizzare i documenti:

  • Risorse Hardware: CPU, RAM e soprattutto la velocità di I/O del disco sono fondamentali. Gli SSD sono altamente raccomandati rispetto agli HDD per le loro prestazioni superiori di lettura/scrittura.
  • Configurazione del Cluster: L'allocazione degli shard, le impostazioni di replica e i ruoli dei nodi giocano un ruolo.
  • Strategia di Indicizzazione: Il metodo utilizzato per inviare i dati (ad esempio, richieste di singoli documenti vs. API bulk).
  • Mapping e Tipi di Dati: Come sono definiti i tuoi campi e i loro corrispondenti tipi di dati.
  • Intervallo di Refresh: Quanto spesso i dati diventano visibili per la ricerca.
  • Impostazioni del Translog: Impostazioni di durabilità per le scritture riconosciute.

Ottimizzare le Performance di Indicizzazione: Best Practice

Questa sezione copre strategie attuabili per migliorare il throughput di indicizzazione di Elasticsearch.

1. Sfrutta l'API Bulk

L'ottimizzazione più fondamentale per l'indicizzazione è utilizzare l'API Bulk. Invece di inviare singole richieste di indicizzazione, che comportano overhead di rete e costo di elaborazione per richiesta, l'API Bulk ti permette di inviare una lista di operazioni (index, create, update, delete) in una singola richiesta HTTP. Questo riduce significativamente la latenza di rete e migliora il throughput complessivo.

Best Practice per l'API Bulk:

  • Dimensione del Batch: Sperimenta con le dimensioni del batch. Inizia con payload modesti, poi aumenta mentre monitori la latenza di indicizzazione, la pressione sulla memoria e i rifiuti 429. Il solo conteggio dei documenti non è sufficiente perché un documento può essere minuscolo e un altro può essere di diversi megabyte.
  • Concorrenza: Usa più thread o client asincroni per inviare richieste bulk contemporaneamente. Tuttavia, evita di sovraccaricare il tuo cluster. Monitora l'utilizzo di CPU e I/O per trovare il punto ottimale.
  • Gestione degli Errori: Implementa una robusta gestione degli errori. L'API Bulk restituisce un array di risposte e devi controllare lo stato di ogni operazione.

Esempio di Richiesta Bulk:

{ "index": { "_index": "my-index", "_id": "1" } }
{ "field1": "value1", "field2": "value2" }
{ "index": { "_index": "my-index", "_id": "2" } }
{ "field1": "value3", "field2": "value4" }

2. Ottimizza le Impostazioni di Indicizzazione

Elasticsearch fornisce diverse impostazioni che possono essere regolate per ottimizzare il processo di indicizzazione. Queste sono tipicamente impostate su base per-indice.

Intervallo di Refresh (index.refresh_interval)

L'intervallo di refresh controlla quanto spesso i dati diventano visibili per la ricerca. Comunemente, gli indici attivi si aggiornano circa una volta al secondo quando vengono cercati, ma i valori predefiniti possono variare in base alla versione e al tipo di indice. Durante l'indicizzazione pesante, puoi aumentare questo intervallo per ridurre il lavoro di refresh. Impostarlo a -1 disabilita i refresh automatici, il che significa che i dati non diventeranno ricercabili finché non eseguirai manualmente un refresh o ripristinerai i refresh automatici.

  • Raccomandazione: Per operazioni di indicizzazione bulk, aumenta temporaneamente index.refresh_interval o impostalo a -1 quando la freschezza della ricerca non è richiesta. Dopo che l'operazione bulk è completa, ripristina l'impostazione che usi per il normale comportamento di ricerca ed esegui un refresh manuale se necessario.

Esempio usando l'API delle Impostazioni dell'Indice:

# Disabilita temporaneamente il refresh
PUT /my-index/_settings
{
  "index" : {
    "refresh_interval" : "-1"
  }
}

# ... esegui l'indicizzazione bulk ...

# Riabilita il refresh
PUT /my-index/_settings
{
  "index" : {
    "refresh_interval" : "1s"
  }
}

Durabilità del Translog (index.translog.durability)

Il translog è un log write-ahead che garantisce la durabilità dei dati. Può essere impostato su request (predefinito) o async. Impostarlo su async scarica il translog in modo asincrono, il che può migliorare la velocità di indicizzazione ma comporta un leggero rischio di perdita di dati se un nodo si guasta prima che il translog venga scritto su disco.

  • Raccomandazione: Per scenari di importazione bulk dove la durabilità è meno critica della velocità, async può essere vantaggioso. Considera sempre la tolleranza della tua applicazione alla perdita di dati.

Numero di Repliche (index.number_of_replicas)

Le repliche sono copie dei tuoi shard primari, utilizzate per l'alta disponibilità e il ridimensionamento delle letture. Tuttavia, ogni replica deve elaborare ogni operazione di indicizzazione. Durante i caricamenti iniziali di grandi quantità di dati, impostare index.number_of_replicas a 0 può accelerare significativamente l'indicizzazione. Dopo che i dati sono stati caricati, puoi aumentare il numero di repliche.

Esempio durante il caricamento bulk:

# Imposta temporaneamente le repliche a 0
PUT /my-index/_settings
{
  "index" : {
    "number_of_replicas" : "0"
  }
}

# ... esegui l'indicizzazione bulk ...

# Ripristina le repliche (ad esempio, a 1)
PUT /my-index/_settings
{
  "index" : {
    "number_of_replicas" : "1"
  }
}

3. Ottimizza i Mapping

I mapping definiscono come i documenti e i loro campi vengono memorizzati e indicizzati. Mapping mal progettati possono portare a problemi di performance.

  • Evita il Mapping Dinamico per Grandi Dataset: Sebbene conveniente, il mapping dinamico può portare a esplosioni di mapping e tipi di campo imprevisti. Definisci mapping espliciti per i tuoi indici, specialmente per dati ad alto volume.
  • Scegli Tipi di Dati Appropriati: Usa i tipi di dati più efficienti. Ad esempio, keyword è più efficiente per la corrispondenza esatta di valori rispetto a text se la ricerca full-text non è richiesta.
  • Disabilita Funzionalità Non Necessarie: Se non hai bisogno di funzionalità come norms per un campo specifico (ad esempio, per corrispondenze esatte o aggregazioni), disabilitarle può risparmiare spazio e migliorare la velocità di indicizzazione (norms: false). Allo stesso modo, disabilita doc_values se non necessario per ordinamenti o aggregazioni su un campo. Tuttavia, doc_values sono generalmente vantaggiosi per aggregazioni e ordinamenti, quindi questa è una decisione sfumata.
  • Campo _source: Se non hai bisogno del documento JSON originale, disabilitare _source può risparmiare spazio su disco e un po' di I/O, ma impedisce il reindicizzazione e rende il debugging più difficile. Considera la compressione di _source se lo tieni abilitato.

Esempio di Mapping (con tipi espliciti e norms disabilitate):

PUT /my-index
{
  "mappings": {
    "properties": {
      "timestamp": {"type": "date"},
      "message": {"type": "text", "norms": false},
      "user_id": {"type": "keyword"}
    }
  }
}

4. Considerazioni su Hardware e Infrastruttura

Anche con configurazioni software perfette, un hardware inadeguato limiterà la velocità di indicizzazione.

  • I/O del Disco: Usa SSD veloci. Gli SSD NVMe offrono le migliori prestazioni. Evita lo storage collegato in rete (NAS) per i nodi di indicizzazione se possibile.
  • CPU e RAM: Sono necessari core CPU sufficienti per l'analisi, e molta RAM aiuta con la cache e le prestazioni generali della JVM.
  • Capacità di Ingest e Coordinamento: Per tassi di ingestione molto elevati, considera nodi di ingest dedicati per le pipeline o nodi di coordinamento per il traffico bulk dei client. I nodi dati fanno ancora il vero lavoro di indicizzazione, quindi non privarli di CPU, memoria o I/O del disco.
  • Rete: Assicura larghezza di banda sufficiente e bassa latenza tra i tuoi client e i nodi Elasticsearch, e tra i nodi nel cluster.

5. Dimensionamento e Conteggio degli Shard

Sebbene non sia direttamente un'impostazione di indicizzazione, il numero e la dimensione degli shard influiscono sulle performance. Troppi shard piccoli possono aumentare l'overhead. Al contrario, un singolo shard massiccio può essere difficile da gestire e potrebbe non scalare bene. Punta a dimensioni degli shard tra 10GB e 50GB per prestazioni ottimali, ma questo può variare.

  • Raccomandazione: Pianifica il numero di shard primari prima di indicizzare grandi quantità di dati. Generalmente non è raccomandato cambiare il numero di shard primari su un indice esistente senza reindicizzare.

6. Gestione del Ciclo di Vita dell'Indice (ILM)

Per dati di serie temporali, l'uso della Gestione del Ciclo di Vita dell'Indice (ILM) è cruciale. Sebbene ILM aiuti principalmente a gestire gli indici nel tempo (rollover, shrink, delete), l'azione di rollover può essere configurata per creare nuovi indici in base alla dimensione o all'età. Questo garantisce che gli indici rimangano entro intervalli di dimensione ottimali, il che indirettamente avvantaggia le performance di indicizzazione.

  • Rollover: Quando un indice raggiunge una certa dimensione o età, ILM può creare automaticamente un nuovo indice vuoto e reindirizzare l'alias del flusso di dati ad esso. Questo ti permette di ottimizzare le impostazioni per il nuovo indice (ad esempio, repliche inferiori durante il caricamento bulk iniziale) e mantenere gli indici attivi gestibili.

Conclusione Pratica

Inizia con l'indicizzazione bulk, mapping espliciti e I/O del disco sufficiente. Per caricamenti una tantum, rilassa refresh e repliche solo mentre puoi tollerare una freschezza di ricerca ridotta o ridondanza, poi ripristina le impostazioni normali e verifica la salute del cluster. Continua a testare con i tuoi documenti reali; dimensioni di batch e conteggi di shard generici sono solo punti di partenza.