La modélisation des données dans Elasticsearch n'est pas aussi évidente que lorsqu'il s'agit de bases de données relationnelles. Contrairement aux bases de données relationnelles traditionnelles qui s'appuient sur la normalisation des données et les jointures SQL, Elasticsearch nécessite des approches alternatives pour gérer les relations.
Il existe quatre solutions courantes pour gérer les relations dans Elasticsearch :
Jointures côté application
Dénormalisation des données
Types de champs imbriqués et requêtes imbriquées
Relations parents-enfants
Dans ce blog, nous verrons comment concevoir votre modèle de données pour gérer les relations à l'aide du type de champ imbriqué et des relations parent-enfant. Nous aborderons l'architecture, les implications en termes de performances et les cas d'utilisation de ces deux techniques.
Elasticsearch prend en charge les structures imbriquées, dans lesquelles les objets peuvent contenir d'autres objets. Les types de champs imbriqués sont des objets JSON dans le document principal, qui peuvent avoir leurs propres champs et types distincts. Ces objets imbriqués sont traités comme des documents distincts et masqués accessibles uniquement à l'aide d'une requête imbriquée.
Les types de champs imbriqués conviennent bien aux relations dans lesquelles l'intégrité des données, le couplage étroit et la structure hiérarchique sont importants. Il s'agit notamment des relations un-à-un et un-à-plusieurs dans lesquelles il existe une entité principale. Par exemple, représenter une personne et ses multiples adresses et numéros de téléphone dans un seul document.
Avec les types de champs imbriqués, Elasticsearch stocke l'intégralité du document, ainsi que les objets parents et imbriqués, sur un seul bloc et segment Lucene. Cela peut entraîner des vitesses de requête plus rapides puisque la relation est contenue dans un document.
Regardons un exemple d'article de blog avec des commentaires. Nous souhaitons imbriquer les commentaires sous l'article de blog afin qu'ils puissent être facilement interrogés ensemble dans le même document.
{ "post_id": "1", "title": "Introduction to Elasticsearch Data Modeling", "content": "Exploring various data modeling options in Elasticsearch.", "comments": [ { "comment_id": "101", "text": "Great overview of data modeling!" }, { "comment_id": "102", "text": "Looking forward to more content." } ] }
Les avantages des relations d'objets imbriquées incluent :
Inefficacité de la mise à jour : les mises à jour, les insertions et les suppressions sur n'importe quelle partie d'un document contenant des objets imbriqués nécessitent la réindexation de l'intégralité du document, ce qui peut être gourmand en mémoire, en particulier si les documents sont volumineux ou si les mises à jour sont fréquentes.
Performances des requêtes avec de grands champs imbriqués : si vous avez des documents avec des champs imbriqués particulièrement grands, cela peut avoir une implication en termes de performances. En effet, la demande de recherche récupère l'intégralité du document.
Plusieurs niveaux d'imbrication peuvent devenir complexes : L'exécution de requêtes sur des structures imbriquées comportant plusieurs niveaux peut encore devenir complexe. En effet, les requêtes peuvent impliquer des requêtes imbriquées dans des requêtes imbriquées, ce qui conduit à un code moins lisible.
Dans le mappage parent-enfant, les documents sont organisés en types parent et enfant. Chaque document enfant a une association directe avec un document parent. Cette relation est établie via une valeur de champ spécifique dans le document enfant qui correspond à l'ID du parent. Le modèle parent-enfant adopte une approche décentralisée dans laquelle les documents parent et enfant existent indépendamment.
Les jointures parent-enfant conviennent aux relations un-à-plusieurs ou plusieurs-à-plusieurs entre entités. Imaginez une application dans laquelle vous souhaitez créer des relations entre des entreprises et des contacts et rechercher des entreprises et des contacts ainsi que des contacts dans des entreprises spécifiques.
Elasticsearch rend les jointures parent-enfant performantes en gardant une trace des parents connectés à quels enfants et en faisant en sorte que les deux entités résident sur la même partition. En localisant l'opération de jointure, Elasticsearch évite le besoin d'une communication étendue entre les partitions, qui peut constituer un goulot d'étranglement en termes de performances.
Prenons l'exemple d'une relation parent-enfant pour les articles de blog et les commentaires. Chaque article de blog, c'est-à-dire le parent, peut avoir plusieurs commentaires, c'est-à-dire les enfants. Pour créer la relation parent-enfant, indexons les données comme suit :
PUT my-index-000001 { "mappings": { "properties": { "post_id": { "type": "keyword" }, "post_id": { "type": "join", "relations": { "post": "comment" } } } } }
Un document parent serait un message qui ressemble à ceci :
{ "post_id": "1", "title": "Introduction to Elasticsearch Data Modeling", "content": "Exploring various data modeling options in Elasticsearch." }
Le document enfant serait alors un commentaire contenant le post_id le liant à son parent.
{ "comment_id": "101", "text": "Great overview of data modeling!", "post_id": "1" }
Les avantages de la modélisation parent-enfant comprennent :
Ressemble à un modèle de données relationnelles : dans les relations parent-enfant, les documents parent et enfant sont séparés et sont liés par un identifiant parent unique. Cette configuration est plus proche d'un modèle de base de données relationnelle et peut être plus intuitive pour ceux qui connaissent ces concepts.
Efficacité de la mise à jour : les documents enfants peuvent être ajoutés, modifiés ou supprimés sans affecter le document parent ou les autres documents enfants. Ceci est particulièrement utile lorsqu'il s'agit de traiter un grand nombre de documents enfants nécessitant des mises à jour fréquentes. Notez que l'association d'un document enfant à un parent différent est un processus plus complexe car le nouveau parent peut se trouver sur une autre partition.
Mieux adapté aux enfants hétérogènes : étant donné que les documents enfants sont stockés séparément, ils peuvent être plus efficaces en termes de mémoire et de stockage, en particulier dans les cas où il existe de nombreux documents enfants présentant des différences de taille significatives.
Les inconvénients des relations parent-enfant comprennent :
Requêtes coûteuses et lentes : la jonction de documents sur des index distincts ajoute du travail de calcul lors de l'exécution des requêtes, ce qui a également un impact sur les performances. Elasticsearch note que les requêtes parent-enfant peuvent être 5 à 10 fois plus lentes que l'interrogation d'objets imbriqués.
Surcharge de mappage : les relations parent-enfant peuvent consommer plus de ressources de mémoire et de cache. Elasticsearch gère une carte des relations parent-enfant, qui peut devenir volumineuse et consommer une mémoire importante, en particulier avec un volume élevé de documents.
Gestion de la taille des partitions : étant donné que les documents parents et enfants résident sur la même partition, il existe un risque potentiel de répartition inégale des données dans le cluster. Certaines partitions peuvent devenir beaucoup plus volumineuses que d'autres, en particulier s'il existe des documents parents contenant de nombreux enfants. Cela peut entraîner des défis dans la gestion et la mise à l'échelle du cluster Elasticsearch .
Réindexation et maintenance du cluster : Si vous devez réindexer des données ou modifier la stratégie de partitionnement , la relation parent-enfant peut compliquer ce processus. Vous devrez vous assurer que l’intégrité de la relation est maintenue lors de telles opérations. Les tâches de maintenance de routine du cluster, telles que le rééquilibrage des partitions ou les mises à niveau des nœuds, peuvent devenir plus complexes. Des précautions particulières doivent être prises pour garantir que les relations parents-enfants ne soient pas perturbées au cours de ces processus.
Elastic , la société derrière Elasticsearch, vous recommandera toujours d'effectuer des jointures côté application, une dénormalisation des données et/ou des objets imbriqués avant de s'engager dans la voie des relations parent-enfant.
Le tableau ci-dessous fournit un récapitulatif des caractéristiques des types de champs imbriqués et des requêtes ainsi que des relations parent-enfant pour comparer les approches de modélisation de données côte à côte.
| Types de champs imbriqués et requêtes imbriquées | Relations parents-enfants |
---|---|---|
Définition | Imbrique un objet dans un autre objet | Relie les documents parents et enfants entre eux |
Des relations | Un à un, un à plusieurs | Un à plusieurs, plusieurs à plusieurs |
Vitesse des requêtes | Généralement plus rapide que les relations parent-enfant car les données sont stockées dans le même bloc et le même segment | Généralement 5 à 10 fois plus lent que les objets imbriqués, car les documents parents et enfants sont joints au moment de la requête. |
Flexibilité des requêtes | Moins flexible que les requêtes parent-enfant car elle limite la portée de l'interrogation aux limites de chaque objet imbriqué | Offre plus de flexibilité dans les requêtes car les documents parents ou enfants peuvent être interrogés ensemble ou séparément |
Mises à jour des données | La mise à jour des objets imbriqués a nécessité la réindexation de l'intégralité du document | La mise à jour des documents enfants est plus facile car elle ne nécessite pas la réindexation de tous les documents |
Gestion | Une gestion plus simple puisque tout est contenu dans un seul document | Plus complexe à gérer en raison de l'indexation séparée et du maintien des relations entre les documents parents et enfants |
Cas d'utilisation | Stockez et interrogez des données complexes avec plusieurs niveaux de hiérarchie | Relations dans lesquelles il y a peu de parents et beaucoup d'enfants, comme les produits et les critiques de produits |
Bien qu'Elasticsearch propose plusieurs solutions de contournement aux jointures de style SQL , notamment les requêtes imbriquées et les relations parent-enfant, il est établi que ces modèles ne sont pas bien évolutifs. Lors de la conception d'applications à grande échelle, il peut être judicieux d'envisager une approche alternative avec des capacités de jointure SQL natives, Rockset .
Rockset est une base de données de recherche et d'analyse conçue pour la recherche SQL, les agrégations et les jointures sur toutes les données, y compris les données JSON profondément imbriquées. Au fur et à mesure que les données sont diffusées dans Rockset, elles sont codées dans les structures de données de base de la base de données utilisées pour stocker et indexer les données pour une récupération rapide. Rockset indexe les données de manière à permettre des requêtes rapides, y compris des jointures, à l'aide de son optimiseur de requêtes basé sur SQL. Par conséquent, aucune modélisation de données initiale n’est requise pour prendre en charge les jointures SQL.
L'un des défis d'Elasticsearch est de savoir comment préserver la relation de manière efficace lorsque les données sont mises à jour. L'une des raisons est qu'Elasticsearch repose sur Apache Lucene, qui stocke les données dans des segments immuables, ce qui nécessite une réindexation de tous les documents. Rockset utilise RocksDB, un magasin de valeurs-clés open source par Meta et conçu pour les mutations de données, pour pouvoir prendre en charge efficacement les mises à jour au niveau du champ sans avoir besoin de réindexer des documents entiers.
Comparons l'approche de la relation parent-enfant dans Elasticsearch avec une requête SQL dans Rockset.
Dans l'exemple de relation parent-enfant ci-dessus, nous avons modélisé les publications avec plusieurs commentaires en créant deux types de documents :
publications ou le type de document parent
commentaires ou les types de documents enfants
Nous avons utilisé un identifiant unique, l'identifiant parent, pour établir la relation entre les documents parent et enfant. Au moment de la requête, nous utilisons Elasticsearch DSL pour récupérer les commentaires pour une publication spécifique.
Dans Rockset, les données contenant les publications seraient stockées dans une collection, une table dans le monde relationnel, tandis que les données contenant des commentaires seraient stockées dans une collection distincte. Au moment de la requête, nous réunirions les données à l’aide d’une requête SQL.
Voici les deux approches côte à côte :
POST /blog/posts/1 { "title": "Elasticsearch Modeling", "content": "A post about data modeling in Elasticsearch" } POST /blog/comments/2?parent=1 { "text": "Great post!" } POST /blog/comments/3?parent=1 { "text": "I learned a lot from this." }
Pour récupérer un article par son titre et tous ses commentaires, vous devrez créer une requête comme suit.
GET /posts/_search { "query": { "bool": { "must": [ { "match": { "title": "Exploring Elasticsearch Models" } } ] } }, "inner_hits": { "_source": ["text"], "name": "comments", "path": "comments" } }
Pour ensuite interroger ces données, il vous suffit d’écrire une simple requête SQL.
SELECT p.title, p.content, c.text FROM posts p JOIN comments c ON p.post_id = c.post_id WHERE p.post_id = 1;
Si vous disposez de plusieurs ensembles de données qui doivent être joints pour votre application, Rockset est plus simple et évolutif qu'Elasticsearch. Cela simplifie également les opérations puisque vous n'avez pas besoin de remodeler vos données, de gérer les mises à jour ou de réindexer les opérations.
Ce blog a fourni une présentation des types de champs imbriqués et des requêtes imbriquées, ainsi que des relations parent-enfant dans Elasticsearch, dans le but de vous aider à déterminer la meilleure approche de modélisation des données pour votre charge de travail.
Les types de champs imbriqués et les requêtes sont utiles pour les relations un-à-un ou un-à-plusieurs où la relation est maintenue au sein d'un seul document. Ceci est considéré comme une approche plus simple et plus évolutive de la gestion des relations.
Le modèle de relation parent-enfant est mieux adapté aux relations un-à-plusieurs à plusieurs-à-plusieurs, mais il s'accompagne d'une complexité accrue, d'autant plus que les relations doivent être contenues dans une partition spécifique.
Si l’une des principales exigences de votre application est la modélisation des relations, il peut être judicieux d’envisager Rockset. Rockset simplifie la modélisation des données et propose une approche plus évolutive de la gestion des relations à l'aide des jointures SQL. Vous pouvez comparer les performances d'Elasticsearch et de Rockset en commençant dès aujourd'hui un essai gratuit avec 300 $ de crédits.