Bonnes pratiques pour optimiser les requêtes Spanner Graph

Ce document décrit les bonnes pratiques pour optimiser les performances des requêtes Spanner Graph, qui incluent les optimisations suivantes :

  • Évitez une analyse complète de la table d'entrée pour les nœuds et les arêtes.
  • Réduisez la quantité de données que la requête doit lire à partir du stockage.
  • Réduisez la taille des données intermédiaires.

Commencer par les nœuds de cardinalité inférieure

Écrivez la traversée de chemin de sorte qu'elle commence par les nœuds de cardinalité inférieure. Cette approche permet de limiter la taille de l'ensemble de résultats intermédiaires et d'accélérer l'exécution des requêtes.

Par exemple, les requêtes suivantes ont la même sémantique :

  • Parcours des arêtes vers l'avant :

    GRAPH FinGraph
    MATCH (p:Person {name:"Alex"})-[:Owns]->(a:Account {is_blocked: true})
    RETURN p.id AS person_id, a.id AS account_id;
    
  • Traversée d'arête inversée :

    GRAPH FinGraph
    MATCH (a:Account {is_blocked:true})<-[:Owns]-(p:Person {name: "Alex"})
    RETURN p.id AS person_id, a.id AS account_id;
    

En supposant qu'il y ait moins de personnes portant le nom Alex que de comptes bloqués, nous vous recommandons d'écrire cette requête dans la traversée d'arête avant.

Il est particulièrement important de commencer par les nœuds de cardinalité inférieure pour le parcours de chemin de longueur variable. L'exemple suivant montre la méthode recommandée pour trouver les comptes qui se trouvent à trois transferts d'un compte donné.

GRAPH FinGraph
MATCH (:Account {id: 7})-[:Transfers]->{1,3}(a:Account)
RETURN a.id;

Spécifier tous les libellés par défaut

Spanner Graph infère les nœuds et les libellés d'arêtes éligibles si les libellés sont omis. Nous vous recommandons de spécifier des libellés pour tous les nœuds et toutes les arêtes dans la mesure du possible, car cette inférence n'est pas toujours possible et peut entraîner l'analyse d'un nombre de libellés supérieur à celui nécessaire.

Instruction MATCH unique

L'exemple suivant recherche les comptes associés par au maximum trois transferts à partir du compte donné :

GRAPH FinGraph
MATCH (src:Account {id: 7})-[:Transfers]->{1,3}(dst:Account)
RETURN dst.id;

Dans les instructions MATCH

Spécifiez des libellés sur les nœuds et les arêtes lorsqu'ils font référence au même élément, mais qu'ils se trouvent dans des instructions MATCH différentes.

L'exemple suivant illustre cette approche recommandée :

GRAPH FinGraph
MATCH (acct:Account {id: 7})-[:Transfers]->{1,3}(other_acct:Account)
RETURN acct, COUNT(DISTINCT other_acct) AS related_accts
GROUP BY acct

NEXT

MATCH (acct:Account)<-[:Owns]-(p:Person)
RETURN p.id AS person, acct.id AS acct, related_accts;

Utiliser IS_FIRST pour optimiser les requêtes

Vous pouvez utiliser la fonction IS_FIRST pour améliorer les performances des requêtes en échantillonnant les arêtes et en limitant les traversées dans les graphiques. Cette fonction permet de gérer les nœuds à cardinalité élevée et d'optimiser les requêtes multihops.

Si la taille de l'échantillon que vous avez spécifiée est trop petite, il est possible que la requête ne renvoie aucune donnée. Pour cette raison, vous devrez peut-être essayer différentes tailles d'échantillon pour trouver l'équilibre optimal entre les données renvoyées et l'amélioration des performances des requêtes.

Ces exemples IS_FIRST utilisent FinGraph, un graphique financier avec Account nœuds et Transfers arêtes pour les transferts d'argent. Pour créer le FinGraph et l'utiliser pour exécuter les exemples de requêtes, consultez Configurer et interroger Spanner Graph.

Limiter les arêtes traversées pour améliorer les performances des requêtes

Lorsque vous interrogez des graphiques, certains nœuds peuvent avoir un nombre d'arêtes entrantes ou sortantes beaucoup plus élevé que d'autres nœuds. Ces nœuds à cardinalité élevée sont parfois appelés super-nœuds ou nœuds hubs. Les super-nœuds peuvent entraîner des problèmes de performances, car les traversées peuvent impliquer le traitement d'énormes quantités de données, ce qui entraîne un déséquilibre des données et de longs temps d'exécution.

Pour optimiser une requête de graphique avec des super-nœuds, utilisez la fonction IS_FIRST dans une clause FILTER afin de limiter le nombre d'arêtes que la requête traverse à partir d'un nœud. Étant donné que les comptes dans FinGraph peuvent comporter un nombre de transactions beaucoup plus élevé que d'autres, vous pouvez utiliser IS_FIRST pour éviter une requête inefficace. Cette technique est particulièrement utile lorsque vous n'avez pas besoin d'une énumération complète de toutes les connexions à partir d'un super nœud.

La requête suivante recherche les comptes (a2) qui reçoivent des transferts directement ou indirectement à partir de comptes bloqués (a1). Elle utilise IS_FIRST pour éviter les ralentissements lorsqu'un compte comporte de nombreux transferts, en limitant le nombre d'arêtes Transfers à prendre en compte pour chaque Account.

GRAPH FinGraph
MATCH
(a1:Account {is_blocked: true})
-[e:Transfers WHERE e IN
  {
    MATCH -[selected_e:Transfers]->
    FILTER IS_FIRST(@max_transfers_per_account) OVER (
      PARTITION BY SOURCE_NODE_ID(selected_e)
      ORDER BY selected_e.create_time DESC)
    RETURN selected_e
  }
]->{1,5}
(a2:Account)
RETURN a1.id AS src_id, a2.id AS dst_id;

Cet exemple utilise les éléments suivants :

  • @max_transfers_per_account : paramètre de requête qui spécifie le nombre maximal d'arêtes Transfers à prendre en compte pour chaque compte (a1).

  • PARTITION BY SOURCE_NODE_ID(selected_e) : garantit que la limite IS_FIRST s'applique indépendamment à chaque compte (a1).

  • ORDER BY selected_e.create_time DESC : spécifie que les transferts les plus récents sont renvoyés.

Échantillonner les nœuds intermédiaires pour optimiser les requêtes multihops

Vous pouvez également améliorer l'efficacité des requêtes en utilisant IS_FIRST pour échantillonner les nœuds intermédiaires dans les requêtes multihops. Cette technique améliore l'efficacité en limitant le nombre de chemins que la requête examine pour chaque nœud intermédiaire. Pour ce faire, divisez une requête multihop en plusieurs instructions MATCH séparées par NEXT, puis appliquez IS_FIRST au point médian où vous devez échantillonner :

GRAPH FinGraph
MATCH (a1:Account {is_blocked: true})-[e1:Transfers]->(a2:Account)
FILTER IS_FIRST(1) OVER (PARTITION BY a2)
RETURN a1, a2

NEXT

MATCH (a2)-[e2:Transfers]->(a3:Account)
RETURN a1.id AS src_id, a2.id AS mid_id, a3.id AS dst_id;

Pour comprendre comment IS_FIRST optimise cette requête :

  • La clause FILTER IS_FIRST(1) OVER (PARTITION BY a2) est appliquée dans la première instruction MATCH.

  • Pour chaque nœud de compte intermédiaire (a2), IS_FIRST ne tient compte que de la première arête Transfers entrante (e1), ce qui réduit le nombre de chemins à explorer dans la deuxième instruction MATCH.

  • L'efficacité globale de la requête à deux sauts est améliorée, car le deuxième MATCH ne traite pas les données inutiles, en particulier lorsque a2 comporte de nombreux transferts entrants.

Étapes suivantes