Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 37 additions & 32 deletions _posts/2012-08-21-au-coeur-d-elasticsearch.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ title: Au coeur d'ElasticSearch
author: filirom1
tags: [index, elasticsearch, lucene]
---
Au cœur d'[ElasticSearch](http://www.elasticsearch.org/) il y a le moteur d'indexation [Lucene](http://lucene.apache.org/), et autour de Lucene il y a plusieurs mécanismes afin de rendre le système scalable et tolérant aux pannes. Commençons par comprendre comment Lucene fonctionne, et nous finirons par les mécanismes de distribution et de tolérance aux pannes mis en place dans ElasticSearch:
Au cœur d'[ElasticSearch](http://www.elasticsearch.org/) il y a le moteur d'indexation [Lucene](http://lucene.apache.org/),
et autour de Lucene il y a plusieurs mécanismes afin de rendre le système scalable et tolérant aux pannes.
Ce sont ces deux parties qui m'ont intéressé pour cet article.

Je ne suis pas contributeur sur le projet, ma vision est donc limitée à ce que j'ai compris en utilisant ElasticSearch,
ou simplement en lisant [la mailing list](https://groups.google.com/forum/?fromgroups#!forum/elasticsearch), le [site officiel](http://www.elasticsearch.org/) ou des articles de blog.
Vous trouverez dans cet article un certain nombre de lien pointant sur les ressources m'ayant éclairée.

<p class="center">
<img src="/public/img/2012-08-21-au-coeur-d-elasticsearch/eslogo.png" border="0" />
Expand Down Expand Up @@ -48,7 +54,7 @@ Une fois le texte analysé et transformé, il faut maintenant le stocker dans un

Le contenu de l'index inversé ressemblera à ça:

Inverted Index:
Index Inversé:

"road" : {Doc1}
"distributed" : {Doc1}
Expand All @@ -61,7 +67,7 @@ Maintenant opérons de même pour la phrase suivante : Doc2 = 'ElasticSearch a d

L'index inversé ressemble maintenant à ça:

Inverted Index:
Index Inversé:

"road" : {Doc1}
"distributed" : {Doc1, Doc2}
Expand All @@ -77,26 +83,25 @@ Un index inversé peut être vu comme une base [clé: multiples valeurs](http://

Maintenant que nous avons une base clé - multiple valeurs, il est assez facile de faire une recherche sur un terme:

Get from index: "engine" => {Doc1,Doc2}
Récupérer dans l'index: "engine" => {Doc1,Doc2}

Les IDs des documents nous sont retournés.

## Indexer != Stocker

Si nous souhaitons que ce soit le document et non l'ID qui soit retourné, il faut stocker le document à côté.

Storage:
Stockage:

"Doc1":"Road to a Distributed Search Engine"
"Doc2":"ElasticSearch a distributed, RESTful Search Engine"

Il faut donc interroger les deux bases : l'index inversé puis la base clé-valeur contenant les documents.

Get from index: "engine" => {Doc1,Doc2}
Get from storage: Doc1 AND Doc2 => ["Road to a Distributed Search Engine", "ElasticSearch a distributed, RESTful Search Engine"]

Récupérer dans l'index: "engine" => {Doc1,Doc2}
Récupérer dans la base de stockage: Doc1 ET Doc2 => ["Road to a Distributed Search Engine", "ElasticSearch a distributed, RESTful Search Engine"]

Mais le fait de stocker le document en plus de l'index est couteux en mémoire. Nous pouvons donc imaginer fonctionner sans stockage de documents : [ElasticSearch-source-field](http://www.elasticsearch.org/guide/reference/mapping/source-field.html), [Lucene-store-field](http://lucene.apache.org/core/3_6_0/api/all/org/apache/lucene/document/Field.Store.html)
Par défaut ElasticSearch [index et stocke](http://www.elasticsearch.org/guide/reference/mapping/source-field.html) la donnée de manière completement transparente pour l'utilisateur, mais ceci est [configurable](https://groups.google.com/d/msg/elasticsearch/k_YgO8xspXE/eqY_SHEwgCMJ).


## Requêtes et Analysers
Expand All @@ -105,23 +110,23 @@ Lors de l'indexation du premier document : 'Road to a Distributed Search Engine'

Si nous recherchons les mots initiaux dans l'index inversé, nous n'obtiendrons aucun résultat:

Get from index: "Road" => {}
Get from index: "Distributed" => {}
Récupérer dans l'index: "Road" => {}
Récupérer dans l'index: "Distributed" => {}

Les mots avaient été indexés en minuscule.

Il est donc important d'appliquer les mêmes Analysers pour l'indexation et la recherche. C'est le comportement par défaut dans ElasticSearch.

Search: "Road" => {}
Get from index: "road => {Doc1}
Rechercher: "Road" => {}
Récupérer dans l'index: "road => {Doc1}


## Syntaxe de requête


Et si nous cherchions "(road OR path) AND search"

Inverted Index:
Index Inversé:

"road" : {Doc1}
"distributed" : {Doc1, Doc2}
Expand All @@ -132,12 +137,12 @@ Inverted Index:

Query:

Get from index: "road => {Doc1}
Get from index: "path => {}
Evaluate: (road OR path) => {Doc1}
Get from index: "search => {Doc1, Doc2}
Evaluate: (road OR path) AND search => {Doc1}
Return => {Doc1}
Récupérer dans l'index: "road => {Doc1}
Récupérer dans l'index: "path => {}
Evaluer: (road OU path) => {Doc1}
Récupérer dans l'index: "search => {Doc1, Doc2}
Evaluer: (road OU path) ET search => {Doc1}
Retourner => {Doc1}

A partir d'un index inversé nous pouvons commencer à construire des requêtes complexes
[Syntaxe des requêtes Lucene](http://lucene.apache.org/core/3_6_1/queryparsersyntax.html).
Expand Down Expand Up @@ -169,12 +174,12 @@ Mais en réalité Lucene permet d'indexer des objets complexes, et faire des rec
}

Lucene permet d'indexer du texte mais pas seulement; il permet aussi d'indexer des nombres, des tableaux, des objets de géo-distances, ...
Voici la liste des [types primitifs](http://lucene.apache.org/core/3_6_1/fileformats.html#Primitive Types) indexables dans Lucene.
Voici la liste des [types primitifs](http://lucene.apache.org/core/3_6_1/fileformats.html#Primitive_Types) indexables dans Lucene.


### Structuration des fichiers Lucene

Une base Lucene est constituée d'un certain nombre de [fichiers](http://lucene.apache.org/core/3_6_1/fileformats.html#file-names):
[Une base Lucene est constituée d'un certain nombre de fichiers](http://lucene.apache.org/core/3_6_1/fileformats.html#file-names):

$ cd /DATA/smartdata/search-bench/nodes/0/indices/4e60bb2aeea3ef8c39000001/0/index
$ ls -lh _2u*
Expand All @@ -191,7 +196,7 @@ Une base Lucene est constituée d'un certain nombre de [fichiers](http://lucene.
* .fdx : Index permettant de retrouver les champs dans le fichier .fdt
* .fnm : Contient le nom des champs
* .frq : Index inversé qui contient également la fréquence d'occurrence de chaque terme
* .nrm : Normalise l'importance de chaque terme relativement à la longueur du texte, ou via un facteur de [boost](http://lucene.apache.org/core/3_6_1/queryparsersyntax.html#Boosting%20a%20Term)
* .nrm : Normalise l'importance de chaque terme relativement à la longueur du texte, ou via un [facteur de boost](http://lucene.apache.org/core/3_6_1/queryparsersyntax.html#Boosting%20a%20Term)
* .prx : Stocke la position des termes dans le texte initiale
* .tii : Fichier complètement chargé en mémoire qui permettra de lire le fichier .tis
* .tis : Dictionnaire des termes
Expand All @@ -214,7 +219,7 @@ Ces fichiers qui constituent [un segment](http://lucene.apache.org/core/3_6_0/fi

Tout ajout de données sera fait dans un nouveau segment.

Le premier segment s'appelle _1.extention, puis ensuite _2.extension, ..., _a.extension, ..., _z.extension, _11.extension, ... suivant une [base 36](http://en.wikipedia.org/wiki/Base_36).
Le premier segment s'appelle \_1.extention, puis ensuite \_2.extension, ..., \_a.extension, ..., \_z.extension, \_11.extension, ... suivant une [base 36](http://en.wikipedia.org/wiki/Base_36).

Lorsque le nombre de segments devient trop important, il devient nécessaire de merger plusieurs segments.

Expand Down Expand Up @@ -279,7 +284,7 @@ La donnée est d'abord écrite dans un fichier de log avant d'être indexée en

Ainsi, si une coupure de courant a lieu lorsque des données sont en RAM, au redémarrage, ElasticSearch charge en RAM le contenu du Translog. Ainsi, le nœud se retrouve dans le même état qu'avant.

ElasticSearch a des [options](http://www.elasticsearch.org/guide/reference/index-modules/Translog.html) pour configurer la fréquence des commits en fonction de l'état du Translog.
ElasticSearch a des [options pour configurer la fréquence des commits](http://www.elasticsearch.org/guide/reference/index-modules/Translog.html) en fonction de l'état du Translog.

Par défaut le [Translog ne fsync pas à chaque opération mais toute les 5s](https://github.com/elasticsearch/elasticsearch/blob/master/src/main/java/org/elasticsearch/index/gateway/local/LocalIndexShardGateway.java#L75). Mais ceci est [réglable](http://markmail.org/thread/lg2rdevj75fh77sy#query:+page:1+mid:pxda5eqquae2ylfm+state:results).

Expand Down Expand Up @@ -311,7 +316,7 @@ Avoir beaucoup de shards implique interroger beaucoup de serveurs à chaque requ
Lorsque l'on indexe une nouvelle donnée dans ElasticSearch, on lui [spécifie un index, un type, et un ID](http://www.elasticsearch.org/guide/reference/api/index_.html).

C'est à partir d'un hash sur le type et sur l'ID que l'on va définir quel shard lui sera attribué (shard = HASH(type, id) MOD nodes ). L'ID étant unique la répartition sur les shards est relativement uniforme.
Il est également possible de forcer l'emplacement de la donnée sur un nœud en fonction de [plusieurs](http://www.elasticsearch.org/guide/reference/modules/cluster.html) [critère](http://www.elasticsearch.org/guide/reference/api/index_.html#Parents & Children)[s](http://www.elasticsearch.org/guide/reference/api/index_.html#Routing).
Il est également possible de forcer l'emplacement de la donnée sur un nœud en fonction de [plusieurs](http://www.elasticsearch.org/guide/reference/modules/cluster.html) [critère](http://www.elasticsearch.org/guide/reference/api/index_.html#Parents_&_Children)[s](http://www.elasticsearch.org/guide/reference/api/index_.html#Routing).

<http://blog.sematext.com/2012/05/29/elasticsearch-shard-placement-control/>

Expand Down Expand Up @@ -363,7 +368,7 @@ En cas d'indexation concurrente de la même donnée (mise à jour de la donnée)

Si le shard primaire tombe, un [réplica sera choisi pour devenir le shard primaire](http://elasticsearch-users.115913.n3.nabble.com/How-does-a-recovering-node-validate-any-shard-information-data-during-recover-td3215028.html).

C'est le rôle du [timeout](http://www.elasticsearch.org/guide/reference/api/index_.html#Timeout) de faire patienter la requête pendant 1 minute le temps qu'un shard primaire soit accessible, et que les autres replicas soit présents ([réglable](http://www.elasticsearch.org/guide/reference/api/admin-indices-update-settings.html)).
C'est le rôle du [timeout de faire patienter la requête pendant 1 minute](http://www.elasticsearch.org/guide/reference/api/index_.html#Timeout) le temps qu'un shard primaire soit accessible, et que les autres replicas soit présents ([réglable](http://www.elasticsearch.org/guide/reference/api/admin-indices-update-settings.html)).

Un nouveau réplica va être créé sur un autre nœud en faisant une copie des donnés du shard primaire, et ainsi le mécanisme d'indexation pourra reprendre son cours.

Expand All @@ -381,7 +386,7 @@ ElasticSearch a besoin d'un nœud maître qui sera [le seul à prendre des déci
Au démarrage du cluster, il y a élection du nœud maître.
Si l'état du cluster a déjà été persisté par un local gateway (Cf redémarrage complet du cluster), le nœud maître [attend ou pas](http://www.elasticsearch.org/guide/reference/modules/gateway/) que l'ensemble des nœuds aient démarré afin de charger l'état du cluster le plus récent.

Tant que le nœud maître est vivant, il s'occupera d'assigner les shards dès qu'un nœud rejoint ou part du cluster. C'est le [nœud maître qui ping les autres nœuds](http://www.elasticsearch.org/guide/reference/modules/discovery/zen.html#Master Election) pour décider s'il les déclare vivants ou morts.
Tant que le nœud maître est vivant, il s'occupera d'assigner les shards dès qu'un nœud rejoint ou part du cluster. C'est le [nœud maître qui ping les autres nœuds](http://www.elasticsearch.org/guide/reference/modules/discovery/zen.html#Master_Election) pour décider s'il les déclare vivants ou morts.

Sinon son rôle se limite à maintenir l'état du cluster et à informer les autres nœuds lorsque l'état change. Les autres nœuds connaissent donc l'état du cluster, ils n'ont pas besoin d'interroger le nœud maître à chaque requête. [Le nœud maître n'est pas un goulot d'étranglement](http://blog.sematext.com/2010/05/03/elastic-search-distributed-lucene/).

Expand All @@ -403,7 +408,7 @@ Sans nœud maître le cluster ne fonctionne pas.

Pendant l'élection chaque nœud connecté fait un vote, et [le broadcast à tout le monde](https://groups.google.com/forum/#!msg/elasticsearch/epdPQ7L9phM/p5xLPco7rX0J). Tous les nœuds reçoivent l'ensemble des résultats, le nœud ayant le plus de points devient le nœud maître.

Chaque nœud ping le nœud maître, et si le maître ne répond plus [pendant un certain temps](http://www.elasticsearch.org/guide/reference/modules/discovery/zen.html#Fault Detection), alors les nœuds recommencent le processus d'élection.
Chaque nœud ping le nœud maître, et si le maître ne répond plus [pendant un certain temps](http://www.elasticsearch.org/guide/reference/modules/discovery/zen.html#Fault_Detection), alors les nœuds recommencent le processus d'élection.

### ElasticSearch et le CAP

Expand All @@ -414,11 +419,11 @@ Par défaut ElasticSearch a choisi de privilégier la cohérence des données.

#### Sans partitionnement

Par défaut ElasticSearch attend avant de répondre à une requête d'indexation qu'[un certain nombre de réplicas](http://www.elasticsearch.org/guide/reference/api/index_.html#Write Consistency) aient indexé la donnée. C'est ce que l'on appelle une réplication synchrone.
Par défaut ElasticSearch attend avant de répondre à une requête d'indexation qu'[un certain nombre de réplicas](http://www.elasticsearch.org/guide/reference/api/index_.html#Write_Consistency) aient indexé la donnée. C'est ce que l'on appelle une réplication synchrone.

Sans partitionnement ElasticSearch a donc choisi la cohérence des données au détriment de la latence.

Mais la réplication peut être [configurée en asynchrone afin de favoriser la latence](http://www.elasticsearch.org/guide/reference/api/index_.html#Asynchronous réplication)
Mais la réplication peut être [configurée en asynchrone afin de favoriser la latence](http://www.elasticsearch.org/guide/reference/api/index_.html#Asynchronous_replication)


#### Avec partionnement
Expand All @@ -440,7 +445,7 @@ Nous pouvons dire à ElasticSearch d'arrêter les clusters ayant [moins de `zen.
Nous nous retrouvons donc avec une moitié de cluster disponible.
Dans cette situation il se peut qu'un rebalancing soit en cours, les index n'auront pas encore tous leurs shards actifs.

ElasticSearch peut être configuré pour [bloquer l'indexation s'il manque des shards](http://www.elasticsearch.org/guide/reference/api/index_.html#Write Consistency), et ainsi garantir une cohérence des données au prix d'une indisponibilité.
ElasticSearch peut être configuré pour [bloquer l'indexation s'il manque des shards](http://www.elasticsearch.org/guide/reference/api/index_.html#Write_Consistency), et ainsi garantir une cohérence des données au prix d'une indisponibilité.


## Prévenir et corriger les erreurs ElasticSearch
Expand Down