Prácticas recomendadas para ajustar las consultas de Spanner Graph

En este documento, se describen las prácticas recomendadas para ajustar el rendimiento de las consultas de Spanner Graph, lo que incluye las siguientes optimizaciones:

  • Evita un análisis completo de la tabla de entrada para nodos y aristas.
  • Reducir la cantidad de datos que la consulta necesita leer del almacenamiento
  • Reduce el tamaño de los datos intermedios.

Comienza con los nodos de menor cardinalidad

Escribe el recorrido de ruta de acceso de modo que comience con los nodos de menor cardinalidad. Este enfoque mantiene pequeño el conjunto de resultados intermedios y acelera la ejecución de la consulta.

Por ejemplo, las siguientes consultas tienen la misma semántica:

  • Recorrido de borde hacia adelante:

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

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

Suponiendo que hay menos personas con el nombre Alex que cuentas bloqueadas, te recomendamos que escribas esta consulta en el recorrido de borde hacia adelante.

Comenzar con nodos de menor cardinalidad es especialmente importante para el recorrido de rutas de longitud variable. En el siguiente ejemplo, se muestra la forma recomendada de encontrar cuentas que estén a tres transferencias de una cuenta determinada.

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

Especificar todas las etiquetas de forma predeterminada

Spanner Graph infiere los nodos aptos y las etiquetas de borde si se omiten las etiquetas. Te recomendamos que especifiques etiquetas para todos los nodos y bordes siempre que sea posible, ya que esta inferencia no siempre es posible y puede hacer que se analicen más etiquetas de las necesarias.

Una sola declaración MATCH

En el siguiente ejemplo, se buscan las cuentas vinculadas por un máximo de 3 transferencias desde la cuenta determinada:

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

En todas las declaraciones MATCH

Especifica etiquetas en los nodos y los bordes cuando se refieran al mismo elemento, pero se encuentren en diferentes instrucciones MATCH.

En el siguiente ejemplo, se muestra este enfoque recomendado:

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;

Usa IS_FIRST para optimizar las preguntas

Puedes usar la función IS_FIRST para mejorar el rendimiento de las consultas tomando muestras de las aristas y limitando los recorridos en los gráficos. Esta función ayuda a controlar los nodos de alta cardinalidad y a optimizar las consultas de múltiples saltos.

Si el tamaño de la muestra que especificaste es demasiado pequeño, es posible que la consulta no devuelva datos. Por este motivo, es posible que debas probar diferentes tamaños de muestra para encontrar el equilibrio óptimo entre los datos devueltos y el rendimiento mejorado de las consultas.

En estos ejemplos de IS_FIRST, se usa FinGraph, un gráfico financiero con nodos Account y aristas Transfers para las transferencias de dinero. Para crear el FinGraph y usarlo para ejecutar las consultas de muestra, consulta Configurar y consultar Spanner Graph.

Limita los bordes recorridos para mejorar el rendimiento de las consultas

Cuando consultas gráficos, algunos nodos pueden tener una cantidad significativamente mayor de aristas entrantes o salientes en comparación con otros nodos. A veces, estos nodos de alta cardinalidad se denominan supernodos o nodos centrales. Los supernodos pueden causar problemas de rendimiento porque los recorridos a través de ellos pueden implicar el procesamiento de grandes cantidades de datos, lo que genera una distorsión de los datos y tiempos de ejecución prolongados.

Para optimizar una consulta de un gráfico con supernodos, usa la función IS_FIRST dentro de una cláusula FILTER para limitar la cantidad de aristas que atraviesa la consulta desde un nodo. Dado que las cuentas de FinGraph pueden tener cantidades significativamente más altas de transacciones que otras, puedes usar IS_FIRST para evitar una consulta ineficiente. Esta técnica es especialmente útil cuando no necesitas una enumeración completa de todas las conexiones desde un supernodo.

La siguiente consulta busca cuentas (a2) que reciben transferencias directa o indirectamente de cuentas bloqueadas (a1). La consulta usa IS_FIRST para evitar un rendimiento lento cuando una cuenta tiene muchas transferencias, ya que limita la cantidad de bordes Transfers que se deben tener en cuenta para cada 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;

En este ejemplo, se usan los siguientes elementos:

  • @max_transfers_per_account: Es un parámetro de consulta que especifica la cantidad máxima de aristas Transfers que se deben tener en cuenta para cada cuenta (a1).

  • PARTITION BY SOURCE_NODE_ID(selected_e): Garantiza que el límite de IS_FIRST se aplique de forma independiente para cada cuenta (a1).

  • ORDER BY selected_e.create_time DESC: Especifica que se devuelven las transferencias más recientes.

Muestrea nodos intermedios para optimizar las consultas de múltiples saltos

También puedes mejorar la eficiencia de las consultas usando IS_FIRST para muestrear nodos intermedios en consultas de múltiples saltos. Esta técnica mejora la eficiencia, ya que limita la cantidad de rutas que la búsqueda considera para cada nodo intermedio. Para ello, divide una consulta de múltiples saltos en varias sentencias MATCH separadas por NEXT y aplica IS_FIRST en el punto medio en el que necesitas realizar el muestreo:

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;

Para comprender cómo IS_FIRST optimiza esta consulta, ten en cuenta lo siguiente:

  • La cláusula FILTER IS_FIRST(1) OVER (PARTITION BY a2) se aplica en la primera instrucción MATCH.

  • Para cada nodo de cuenta intermedio (a2), IS_FIRST solo considera la primera arista entrante Transfers (e1), lo que reduce la cantidad de rutas para explorar en la segunda instrucción MATCH.

  • Se mejora la eficiencia general de la consulta de dos saltos porque el segundo MATCH no procesa datos innecesarios, en especial cuando a2 tiene muchas transferencias entrantes.

¿Qué sigue?