2016-08-14 1 views
1

J'utilise Neo4j pour représenter notre entrepôt de données. Nous avons environ 100 000 noeuds de divers types (~ 10) dont certains ont plusieurs étiquettes. Un sous-ensemble des types de nœuds typiques sont:Stratégie d'index Neo4j optimale pour les nœuds multi-étiquettes

(:User) 
(:Tableau:Workbook) 
(:Tableau:Dashboard) 

Ici Tableau représente le logiciel de visualisation de données et Workbook et Dashboard sont différentes entités de tableau. La raison pour laquelle nous sommes allés avec plusieurs étiquettes plutôt qu'une seule étiquette définie de manière unique était que l'on peut vouloir faire correspondre tous les nœuds (:Tableau) ou tous les nœuds (:Dashboard) (nous avons plusieurs sources de tableau de bord).

J'utilise également la bibliothèque UAID GraphAware Neo4j (https://github.com/graphaware/neo4j-uuid) pour m'assurer que chaque noeud (quel que soit son type) est identifié de manière unique via la propriété de noeud uuid.

J'ai créé un index (et contrainte d'unicité) pour chaque étiquette de noeud pour améliorer les performances, à savoir

CREATE INDEX ON:User(uuid) 
CREATE INDEX ON:Tableau(uuid) 
CREATE INDEX ON:Workbook(uuid) 
CREATE INDEX ON:Dashboard(uuid) 

étant donné que CREATE INDEX doit prendre exactement une étiquette.

J'ai rencontré quelques problèmes de performances lors de la mise en correspondance de noeuds en utilisant Cypher étant donné cette structure d'indexation. Même si la cardinalité de (:Tableau:Dashboard) < < (:Tableau) la requête suivante est suboptimale

MATCH (n:Tableau:Dashboard) WHERE n.uuid = <UUID> 

par rapport à une ou l'autre

MATCH (n:Tableau) WHERE n.uuid = <UUID> 
MATCH (n:Dashboard) WHERE n.uuid = <UUID> 

étant donné que l'ancien ne tirent parti de ne tout indice que le faire plus tard. Ce problème est aggravé si l'on veut trouver un nœud globalement basé uniquement sur l'UUID (qui est unique) ce qui est souvent le cas lorsqu'on utilise une API Flask pour trouver des nœuds, ce qui se traduit par la logique Cypher suivante:

MATCH(n) WHERE n.uuid = <UUID> 

fil suivant suggère la création d'une étiquette de noeud global global Entity et la création d'un index sur cette (Neo4j: Create index for nodes with same property),

CREATE INDEX ON:Entity(uuid) 

si les noeuds maintenant être étiquetés avec comme suit,

(:Entity:User) 
(:Entity:Tableau:Workbook) 
(:Entity:Tableau:Dashboard) 

Est-ce la meilleure approche? Une autre solution consisterait à choisir simplement la première étiquette si plusieurs étiquettes sont définies, étant donné qu'elle est garantie d'être indexée, mais cela ne résout pas le problème de trouver un nœud basé uniquement sur l'UUID. Si j'utilise l'approche de libellé Entity, est-il toujours logique de conserver tous les index définis précédemment, c'est-à-dire que je prévois des améliorations significatives des performances si je ne recherche qu'un petit sous-ensemble de nœuds? Par exemple, si je savais que n était un noeud (:User) dois-je attendre à voir des performances similaires avec,

MATCH (n:Entity) WHERE n.uuid = <UUID> 
MATCH (n:User) WHERE n.uuid = <UUID> 

Ne pas avoir la capacité d'index sur aucun ou plusieurs index est une honte, étant donné que les requêtes optimales Cypher peuvent être plus abstraites , c'est à diredisons qu'un (:Tableau:Workbook) Remplit (:Tableau:Dashboard) alors de trouver les tableaux de bord que le classeur Remplit une interrogerait,

MATCH (s:Tabeau:Workbook)-[:POPULATES]->(t:Tableau:Dashboard) 
WHERE s.uuid = <UUID> 
RETURN t 

qui est assez transparente, mais ce qui suit serait plus optimale du point de vue de la performance, quoique moins transparente étant donné que il est pas évident à l'utilisateur quel type de noeud s est,

MATCH (s:Entity)-[:POPULATES]->(t:Tableau:Dashboard) 
WHERE s.uuid = <UUID> 
RETURN t 

Répondre

0

Vous êtes maintenant des index qui se chevauchent pour Tableau et Workbook et Tableau et Dashboard. Pourquoi ne pas simplement conserver l'index pour Tableau pour éliminer la redondance et lancer un indice avec le planificateur de requête avec USING INDEX pour vous assurer qu'il est utilisé dans votre correspondance. c'est-à-dire quelque chose comme ceci ...

MATCH (s:Tableau:Workbook)-[:POPULATES]->(t:Tableau:Dashboard) 
USING INDEX s:Tableau(uuid) 
WHERE s.uuid = <UUID> 
RETURN t 
+0

Merci. Je ne savais pas que l'on pouvait appliquer l'utilisation de l'index. Je me rends compte que j'ai des index qui se chevauchent, mais est-ce une mauvaise chose? De cette façon, je peux toujours tirer parti d'un index en faisant correspondre n'importe quel type de nœud, c'est-à-dire 'MATCH (n: Dashboard) UTILISATION INDEX n: Dashboard (uuid)' sauf lorsque je ne suis pas certain du type. – John

+0

np. Compte tenu de la petite taille de votre base de données, le fait de conserver des index qui se chevauchent ne va certainement pas poser de problème. Si vous avez une analyse de rentabilisation pour cela et que cela l'emporte sur la surcharge supplémentaire de maintenir un index sur un attribut particulier deux fois alors je dis aller le chercher. –