DB2ElasticStack

Questo documento riporta alcune indicazioni su come caricare dati da DB relazionali su Elasticsearch struttando le applicazioni dell'Elastic Stack.

Dal punto di vista tecnico Elastichsearch e' un database classificato come Search Engine, NoSQL, distribuito; oltre alle funzionalita' di ricerca full test consente anche l'analisi i dati. L'Elastic Stack e' l'insieme dei componenti che formano l'intera architettura per la gestione dei dati in cui Elasticsearch e' la componente di motore di ricerca.

Il documento si riferisce alla versione 6.6 di Elastic Stack ma i contenuti valgono anche per altre versioni.
Questo documento presenta diversi aspetti del caricamento dati da DB realazionali a Elasticsearch: Ho fretta!, Introduzione, Installazione, Versioni ed aggiornamenti, ... con un approccio pratico con molti esempi di comandi e script.

Ho fretta!

Scaricare ed installare Elasticsearch (motore di ricerca), scaricare ed installare Logstash (tool caricamento dati), scrivere il file di configurazione della pipeline esempio1.conf contenente:

input {
  jdbc {
    jdbc_driver_library => "mysql-connector-java-5.1.36-bin.jar"
    jdbc_driver_class => "com.mysql.jdbc.Driver"
    jdbc_connection_string => "jdbc:mysql://localhost:3306/mydb"
    jdbc_user => "mysql"
    jdbc_password => "XXX"
    statement => "select film_id, title, description, release_year, last_update from sakila.film"
  }
}

Lanciare il caricamento con:
 logstash -f esempio1.conf

Kibana: l'interfaccia grafica di Elasticsearch Viene automaticamente creato un indice su Elasticsearch contenente tutti i dati della tabella film. A questo punto i dati sono caricati sull'indice ed e' possibile effettuare ricerche con:

curl 'http://localhost:9200/film/_search'

Il risultato viene restituito in formato JSON e, in questo caso, e' l'intero insieme dei dati.

Naturalmente le ricerche possono essere molto piu' complesse e puo' essere utilizzata l'ottima interfaccia grafica Kibana... continuate a leggere! [NdA ma se davvero non avete pazienza passate a questo paragrafo]

Introduzione

Per effettuare veloci ricerche testuali su basi dati di grandi dimensioni vanno utilizzati strumenti adeguati.
Elasticsearch e' un moderno, potente, distribuito motore di ricerca RESTful basato su Apache Lucene. Lucene e' un ottimo motore per le ricerche full-text realizzato con librerie Java, richiamabile con una semplice e potente API dalle applicazioni e distribuito con una licenza molto libera. Elastichsearch si pone sopra Lucene fornendo un'interfaccia JSON REST, distribuendo i dati in cluster ed aggiungendo una serie di funzionalita' (eg. thread-pool, fields mapping, queues, node/cluster monitoring API, data monitoring API, Cluster management, Users, ...).

Elastic Stack Oltre al motore di indicizzazione e ricerca sono disponibili diversi altri componenti tra loro integrati che vengono definiti nel loro insieme Elastic Stack. I componenti piu' significativi dell'Elastic Stack sono:

Elasticsearch fornisce un'interfaccia RESTful sulla porta 9200 (per default). Per creare un indice basta inserire un primo documento:

POST /companies/_doc { "name" : "XeniaLAB", "address": "Corso Vittorio Emanuele II, 111", "city": "Torino", "postal_code": "10128", "country": "Italy", "share_capital": 10000, "creation_date": "2016-02-04T00:00:00.000", "location" : { "type": "Point", "coordinates": [ 45.067501, 7.665116 ] } }

Tutti i dati vengono passati in formato JSON.

Per effettuare una ricerca il comando e':

GET /companies/_doc/_search { "query": { "match": { "city": "Torino" } } }

L'interfaccia REST contiene diversi comandi: GET, POST, PUT (come il POST ma richiede l'id nell'URL), DELETE (dall'ovvio significato) che, richiamati con le diverse sintassi previste, coprono tutte le funzionalita' di Elasticsearch sia di amministrazione che di gestione dei dati.

La componente di indicizzazione di Elasticsearch e' basata su Apache Lucene: uno dei piu' robusti e completi motori di indicizzazione utilizzati sia direttamente che da altri database (eg. Solr, Neo4j e, ovviamente Elasticsearch). Dal punto di vista dei dati Lucene mantiene gli index files che contengono tutti i dati memorizzati con la tecnica delle liste invertite; invece Elasticsearch gestice i metadati relativi alle funzionalita' costruite sopra Lucene (eg. field mappings, index settings, cluster metadata, ...).
Elasticsearch utilizza una struttura di directory suddivisa per nodes protetta da file node.lock. All'interno sono utilizzati file .st (metadata) ed .si (segmenti) mentre le directory index contengono gli indici di Lucene. La tabella seguente riporta i principali tipi di file utilizzati da Lucene (cfr Lucene Core):

NomeEstensioneDescrizione
Segments File segments_N Stores information about a commit point
Lock File write.lock The Write lock prevents multiple IndexWriters from writing to the same file.
Segment Info .si Stores metadata about a segment
Compound File .cfs, .cfe An optional "virtual" file consisting of all the other index files for systems that frequently run out of file handles.
Fields .fnm Stores information about the fields
Field Index .fdx Contains pointers to field data
Field Data .fdt The stored fields for documents
...

Kibana e' lo strumento per la gestione degli indici, la visualizzazione dei dati e la loro analisi. Ha un'interfaccia web disponibile sulla porta 5601 (per default). Come tutte le interfacce grafiche e' piu' semplice da usare che da descrivere: Kibana: interfaccia grafica per Elasticsearch

Logstash e Beats sono strumenti di ingest dei dati ovvero si occupano di caricare i dati rispettivamente da file o da applicazioni. In realta' le possibilita' di entrambe gli strumenti sono molto piu' ampie e possono essere gestite raccolte di dati praticamente da ogni sorgente di dati utile e con ogni modalita'.

Installazione

L'installazione e' molto semplice e sono previste diverse modalita'. Si parte dal sito ufficiale scegliendo se effettuare il download oppure lanciare il servizio in Cloud. Ogni componente dell'architettura e' indipendente e puo' essere utilizzato o meno a seconda delle esigenze.

In pratica l'installazione on-premises consiste semplicemente nel decomprimere il file scaricato poiche' si tratta di applicazioni Java multipiattaforma [NdA c'e' qulche eccezione: Kibana e' un'applicazione Node.js ma e' ugualmente multipiattaforma].
Naturalmente un prerequisito fondamentale e' l'ambiente Java [NdA e' supportato anche l'OpenJDK].

Su Linux Red Hat/CentOS/Fedora/Oracle Linux/... e' consigliato utilizzare gli RPM e YUM. La configurazione del repository e' molto semplice:

[elasticsearch-6.x]
name=Elasticsearch repository for 6.x packages
# baseurl=https://artifacts.elastic.co/packages/6.x/yum # Full 
baseurl=https://artifacts.elastic.co/packages/oss-6.x/yum # Apache license only
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=1
autorefresh=1
type=rpm-md

Per installare basta lanciare il comando:
 yum install elasticsearch logstash kibana apm-server

Per avviare Elasticsearch (RH/CentOS/OL 7.x):

sudo systemctl daemon-reload sudo systemctl enable elasticsearch.service sudo systemctl start elasticsearch.service

Su macOS l'installazione consigliata e' con Homebrew:

brew install elasticsearch brew install logstash brew install kibana brew services start elasticsearch brew services start kibana

Il file di configurazione di Elasticsearch e' elasticsearc.yml, ma le impostazioni di default sono generalmente sufficienti per un uso come ambiente di test.

Terminata l'installazione ed avviati i servizi, Elasticsearch sara' raggiungibile all'URL http://localhost:9200/ e kibana all'URL http://localhost:5601/.

Utilizzo

Vediamo come effettuare le principali operazioni con elasticsearch:

# Inserendo il primo documento automaticamente si crea un indice
POST /companies/_doc 
{ "name" : "XeniaLAB",
  "address": "Corso Vittorio Emanuele II, 111",
  "city": "Torino",
  "postal_code": "10128",
  "country": "Italy",
  "share_capital": 10000,
  "creation_date": "2016-02-04T00:00:00.000"
}

# E' anche possibile utilizzare il comando PUT ma e' necessario specificare l'ID
PUT /companies/_doc/2 
{
  "name" : "ACME",
  "address": "Corso Sebastopoli, 131",
  "city": "Torino",
  "postal_code": "10136",
  "country": "Italy",
}

# Per leggere i dati si utilizza GET
GET /companies/_doc/_search
{
  "query": {
    "match": {
      "city": "torino"
    }
  }
}

# Ma e' stato introdotto anche l'SQL!
POST /_xpack/sql?format=txt
{
    "query": "SELECT name, address FROM companies ORDER BY share_capital DESC LIMIT 5"
}

# La modifica si effettua con POST indicando la modifica da effettuare
POST /inspections/_doc/2/_update
{
   "doc" : {
      "srl" : true,
      "views": 0
   }
}

# Infine per cancellare... DELETE
DELETE /companies/_doc/2

# Per definire caratteristiche dell'indice diverse da quelle di default
# e' possibile creare l'indice prima di inserire i dati
DELETE /companies
PUT /companies
{
  "settings": {
    "index.number_of_shards": 1,
    "index.number_of_replicas": 0
  }
}

# Mapping
GET /companies/_mapping/_doc

# Text analysis 
GET /companies/_analyze
{
  "tokenizer": "standard",
  "text": "Quel ramo del lago di Como"
}

Chi ha una mente malata dall'SQL come l'autore avra' notato che sono presentati i comandi CRUD e le DDL... ma le cose sono molto diverse con Elasticsearch!

Un buon esempio di comandi e' riportato in questo file: in modo progressivo si vedono i principali comandi di Elasticsearch.

Caricare dati da un DB relazionale

Logstash e' lo strumento di ingest di dati verso Elasticsearch. I caricamenti vengono chiamate pipeline e nel file di configurazione si specifica l'input, l'output e gli eventuali filter della pipeline. Come esempio scriviamo nel file esempio0.conf:

# file: esempio0.conf
input { stdin { } }
output {
  elasticsearch { hosts => ["localhost:9200"] }
  stdout { codec => rubydebug }
}
e lo eseguiamo con:
bin/logstash -f esempio1.conf

I dati introdotti vengono cosi' inseriti su Elasticsearch. Le possibilita' di Logstash sono molto ampie: per esempio nella raccolta dei file di log e' in grado di caricare file differenti ricordando i file gia' caricati ed il punto da cui riprendere i caricamenti...

Ma veniamo al punto: caricare dati da un DB relazionale su Elasticsearch!
Tra i molti plugin di Logstash e' disponibile quello per JDBC che consente di sfruttare in input database relazionali. Ecco come caricare i dati:

# file: esempio1.conf
input {
    jdbc {
        jdbc_connection_string => "jdbc:postgresql://localhost:5432/mydb"
        jdbc_user => "postgres"
        jdbc_password => "XXX"
        jdbc_driver_library => "/path/to/postgresql-9.4-1201.jdbc41.jar"
        jdbc_driver_class => "org.postgresql.Driver"
        statement => "SELECT * from contacts"
    }
}
output {
  elasticsearch { hosts => ["localhost:9200"] }
}

Innanzi tutto si nota che e' possibile indicare una qualsiasi connessione JDBC specificando la jdbc_driver_library: questo consente di accedere a centinaia di database relazionali e non. Gli altri parametri sono i normali parametri di connessione verso la base dati prescelta. Per indicare i dati da caricare... basta una semplice query SQL indicata nel parametro statement. Elasticsearch eseguita' automaticamente il mapping delle colonne individuando i tipi di dati utilizzati.
Nel seguito vedremo le altre possibilita' di Logstash utilizzando semplici esempi.

Per caricare dati da MySQL:

input {
  jdbc {
    jdbc_driver_library => "mysql-connector-java-5.1.36-bin.jar"
    jdbc_driver_class => "com.mysql.jdbc.Driver"
    jdbc_connection_string => "jdbc:mysql://localhost:3306/mydb"
    jdbc_user => "mysql"
    jdbc_password => "XXX"
    statement => "select film_id, title, description, release_year, last_update from sakila.film"
  }
}

Si possono utilizzare parametri per rendere dinamiche le query:

    parameters => { "favorite_director" => "Tarantino" }
    statement => "SELECT * from sakila.film where director = :favorite_director"

E' possibile indicare parametri anche per la parte di Elasticsearch modificando il comportamento di default:

output {
    elasticsearch {
        protocol => http
        index => "movies"
        document_id => "%{film_id}"
        host => "ES_NODE_HOST"
    }

La presenza di NULL e' normale in un DB relazionale ma il NULL non e' previsto in JSON! Il modo corretto per trattare un NULL e' quello di non mettere il campo corrispondente. Per ottenere questo comportamento in Logstash e' possibile utilizzare un filtro:

filter {
    if [DataPagamento] == "" {
        mutate{
        remove_field => ["DataPagamento"]
        }
    }
}

Logstash permette di schedulare la raccolta dati. Il parametro ha la stessa sintassi del crontab, per eseguire il caricamento ogni ora al quinto minuto:

    schedule => "5 * * * *"

Se si impostano i parametri use_column_value e tracking_column Logstash mantiene in sql_last_value l'ultimo valore caricato ed opera in modo differenziale. E' possibile impostare il tracking_column_type (timestamp da Epoc o numeric) e naturalmente va posta attenzione alla logica delle UPDATE/DELETE. Ecco l'esempio:

    use_column_value => true
    tracking_column => "id"

Altri elementi/alternative importanti per caricare dati da DB esterni sono il caricamento di tabelle diverse (ciascuna richiede una sua pipeline), il mapping che consente di specificare i datatype, la separazione in uno o piu' indici, filtri con remove_field/replace, l'utilizzo dei driver JDBC di Elasticsearch, il passaggio di dati su file (davvero un po' retro' come stile ma ovviamente funziona sempre), ... ma sono argomenti che chiederebbero troppo spazio per essere trattati adesso!

Naturalmente maggiori dettagli si trovano nella documentazione ufficiale.

Problemi e workaround

Tutti i tool dell'Elastic Stack riportano segnalazioni ed errori in un log log molto completo [NdA leggi logorroico come per tutte le applicazioni Java]. L'analisi dei log e' quindi sempre il primo passo da effettuare in caso di problemi. E' comunque possibile fornire qualche indicazione generale per i casi piu' comuni.

Versioni ed upgrade

Mantenersi aggiornati con le versioni e' sempre importante... [NdE: serve a rimanere giovani e tutto e' piu' veloce :-]

Questo vale anche per Elasticsearch e per i database Relazionali.
La versione piu' recente di Elasticsearch e' la 6.6 [NdA 2019-01] che introduce la funzionalita' di index lifecycle management e cross-cluster search. Nel documento Your Server Stinks sono mantenute apposite sezioni aggiornate per Elasticsearch:

Version
Status
Features
Last release
Date (from)
Date (last)
Date (EOL)
Notes
7 Alpha 7 alpha22018-12
6 Production Sequence IDs, search scalability, security, migration assistant. Desupport: pre 5.0 indexes.
(6.3 2018-06): Elasticsearch SQL. (6.6 2019-01): index lifecycle management, cross-cluster search
6.6.22017-112019-012020-05
5 Production Unified versioning for Elasticsearch, Logstash, Kibana, and Beats; Painless scripting language; ingest nodes; pipelines. Based on Lucene 6.2. Desupport: pre 2.0 indexes 5.6.142016-102018-122019-03
2 EOL 2.4.62015-102017-062018-02
1 EOL 1.7.62014-022016-112017-01
0 EOL 2010-02

Elasticsearch supporta sempre gli indici della major version precedente. Quindi i dati indicizzati con la versione 5 possono essere letti da qualsiasi installazione con versione 6.
E' invece molto opportuno che i componenti dello stack utilizzino la stessa major e minor release.

Varie ed eventuali

Chi e' abituato a lavorare con i motori di ricerca testuali sa bene che le fasi di indicizzazione e di ricerca sono molto diverse come requisiti e modalita'. Anche se Elasticsearch ha tempi quasi realtime nell'inserimento di nuovi documenti le fasi di ingest sono tipicamente eseguite in modalita' batch e quelle di ricerca sono tipicamente interattive.

Questa pagina e' introduttiva, molte delle piu' interessanti possibilita' di Elastisticsearch non sono state neanche accennate: APM, APM, ...

Per un utilizzo Enterprise le Elastic Stack Features [NdA in precedenza chiamate anche X-Pack] sono sicuramente molto interessanti anche se richiedono una sottoscrizione.



Titolo: DB 2 Elastic Stack
Livello: Medio (2/5)
Data: 31 Ottobre 2018 🎃 Halloween
Versione: 1.0.1 - 14 Febbraio 2019 ❤️ San Valentino
Autore: mail [AT] meo.bogliolo.name