Prácticas recomendadas para diseñar un esquema de grafo de Spanner

En este documento, se describe cómo crear consultas eficientes con las prácticas recomendadas para diseñar esquemas de gráficos de Spanner. El diseño del esquema puede Debe ser iterativo, por lo que recomendamos que primero identifique los patrones críticos para guiar el diseño de tu esquema.

Para obtener información general sobre el diseño de esquemas consulta Prácticas recomendadas para el diseño de esquemas.

Optimiza el recorrido perimetral

El recorrido perimetral es el proceso de navegar por un gráfico mediante el seguimiento bordes, comenzando en un nodo concreto y moviéndose a lo largo de bordes conectados para alcanzar otros nodos. El recorrido perimetral es una operación fundamental en el grafo de Spanner, por lo que Mejorar la eficiencia del recorrido perimetral es clave para el rendimiento de tu aplicación.

Se puede atravesar una arista en dos direcciones: al pasar del nodo fuente a el nodo de destino se denomina recorrido perimetral de avance, mientras se desplaza desde que el nodo de destino al nodo fuente se denomina reverso del borde inverso.

Optimiza el recorrido de borde hacia adelante con la intercalación

Para mejorar el rendimiento del recorrido de borde directo, intercala la tabla de entrada de borde en la tabla de entrada del nodo de origen para colocar bordes con los nodos fuente. La intercalación es una técnica de optimización de almacenamiento en Spanner que coloca físicamente las filas de la tabla secundaria con sus filas superiores correspondientes en y almacenamiento de los datos. Para obtener más información sobre la intercalación, consulta Descripción general de los esquemas.

En el siguiente ejemplo, se muestran estas prácticas recomendadas:

CREATE TABLE Person (
  id               INT64 NOT NULL,
  name             STRING(MAX),
) PRIMARY KEY (id);

CREATE TABLE PersonOwnAccount (
  id               INT64 NOT NULL,
  account_id       INT64 NOT NULL,
  create_time      TIMESTAMP,
) PRIMARY KEY (id, account_id),
  INTERLEAVE IN PARENT Person ON DELETE CASCADE;

Optimizar el recorrido perimetral inverso con clave externa

Para atravesar aristas inversas de manera eficiente, crea una restricción de clave externa entre entre el perímetro y el nodo de destino. Esta clave externa crea automáticamente un índice secundario en el perímetro con la clave de las claves del nodo de destino. El índice secundario es usarse automáticamente durante la ejecución de la consulta.

En el siguiente ejemplo, se muestran estas prácticas recomendadas:

CREATE TABLE Person (
  id               INT64 NOT NULL,
  name             STRING(MAX),
) PRIMARY KEY (id);

CREATE TABLE Account (
  id               INT64 NOT NULL,
  create_time      TIMESTAMP,
) PRIMARY KEY (id);

CREATE TABLE PersonOwnAccount (
  id               INT64 NOT NULL,
  account_id       INT64 NOT NULL,
  create_time      TIMESTAMP,
  CONSTRAINT FK_Account FOREIGN KEY (account_id) REFERENCES Account (id),
) PRIMARY KEY (id, account_id),
  INTERLEAVE IN PARENT Person ON DELETE CASCADE;

Optimiza el recorrido perimetral inverso con el índice secundario

Si no quieres crear una clave externa en el perímetro, por ejemplo, debido a la con la integridad de datos estricta que este aplica, puedes crear un índice secundario la tabla de entrada perimetral, como se muestra en el siguiente ejemplo:

CREATE TABLE PersonOwnAccount (
  id               INT64 NOT NULL,
  account_id       INT64 NOT NULL,
  create_time      TIMESTAMP,
) PRIMARY KEY (id, account_id),
  INTERLEAVE IN PARENT Person ON DELETE CASCADE;

CREATE INDEX Reverse_PersonOwnAccount
ON PersonOwnAccount (account_id);

No permitir bordes colgantes

Un borde colgante es un perímetro que conecta menos de dos nodos. Una prenda colgante perímetro puede ocurrir cuando se borra un nodo sin quitar sus bordes asociados. cuando se crea una arista sin vincularla correctamente a sus nodos.

La inhabilitación de los bordes colgantes proporciona los siguientes beneficios:

  • Aplica la integridad de la estructura del grafo.
  • Mejora el rendimiento de las consultas, ya que evita el trabajo adicional de filtrar los bordes donde no existen extremos.

No permitir los bordes colgantes con restricciones referenciales

Para no permitir bordes colgantes, especifica restricciones en ambos extremos:

  • Intercalar la tabla de entrada perimetral en la tabla de entrada del nodo de origen Esta garantiza que el nodo de origen de un perímetro siempre exista.
  • Crea una restricción de clave externa en los bordes para asegurarte de que el nodo de destino de un borde siempre exista.

En el siguiente ejemplo, se usa la intercalación y una clave externa para aplicar integridad referencial:

CREATE TABLE PersonOwnAccount (
  id               INT64 NOT NULL,
  account_id       INT64 NOT NULL,
  create_time      TIMESTAMP,
  CONSTRAINT FK_Account FOREIGN KEY (account_id) REFERENCES Account (id) ON DELETE CASCADE,
) PRIMARY KEY (id, account_id),
  INTERLEAVE IN PARENT Person ON DELETE CASCADE;

Usa ON DELETE CASCADE para quitar automáticamente los bordes cuando se borre un nodo

Cuando usas la intercalación o una clave externa para no permitir bordes colgantes, usa la cláusula ON DELETE para controlar el comportamiento cuando quieras borrar un nodo con bordes que aún están conectados. Para obtener más información, consulta borrar en cascada de tablas intercaladas y borrar en cascada con claves externas.

Puedes usar ON DELETE de las siguientes maneras:

  • ON DELETE NO ACTION (o bien omitir la cláusula ON DELETE): Borrar una de los nodos con perímetros fallarán.
  • ON DELETE CASCADE: Borrar un nodo quita automáticamente el elemento las aristas asociadas en la misma transacción.

Borrar cascada para las aristas que conectan diferentes tipos de nodos

  • Borra las aristas cuando se borre el nodo de origen. Por ejemplo,INTERLEAVE IN PARENT Person ON DELETE CASCADE borra todas las llamadas salientes. Se borrarán PersonOwnAccount bordes del nodo Person. Para ver más información, consulta Crea tablas intercaladas.

  • Borra las aristas cuando se borre el nodo de destino. Por ejemplo: CONSTRAINT FK_Account FOREIGN KEY(account_id) REFERENCES Account(id) ON DELETE CASCADE borra todos los bordes PersonOwnAccount entrantes. al nodo Account que se borrará. Para obtener más información, consulta Claves externas.

Borrar cascada para las aristas que conectan el mismo tipo de nodos

Cuando los nodos de origen y destino de un perímetro tienen el mismo tipo y la borde está intercalado en el nodo de origen, puedes definir ON DELETE CASCADE solo para el nodo de origen o de destino (pero no para ambos nodos).

Para quitar automáticamente los bordes colgantes en ambos casos, crea una clave externa en la referencia del nodo de origen perimetral, en lugar de intercalar la tabla de entrada perimetral en la tabla de entrada del nodo fuente.

Recomendamos intercalar para Optimiza el recorrido del borde avanzado. Asegúrate de verificar el impacto en tus cargas de trabajo antes de continuar. Consulta el siguiente ejemplo, que usa AccountTransferAccount como la tabla de entrada de borde:

--Define two Foreign Keys, each on one end Node of Transfer Edge, both with ON DELETE CASCADE action:
CREATE TABLE AccountTransferAccount (
  id               INT64 NOT NULL,
  to_id            INT64 NOT NULL,
  amount           FLOAT64,
  create_time      TIMESTAMP NOT NULL,
  order_number     STRING(MAX),
  CONSTRAINT FK_FromAccount FOREIGN KEY (id) REFERENCES Account (id) ON DELETE CASCADE,
  CONSTRAINT FK_ToAccount FOREIGN KEY (to_id) REFERENCES Account (id) ON DELETE CASCADE,
) PRIMARY KEY (id, to_id);

Filtrar por propiedades de perímetro o nodo con índices secundarios

Los índices secundarios son esenciales para un procesamiento eficiente de consultas. Admiten búsquedas rápidas de nodos y aristas según valores de propiedad específicos, sin tener que recorrer toda la estructura del grafo. Esto es importante cuando si trabajas con gráficos grandes, ya que recorrer todos los nodos y las aristas puede ser muy ineficiente.

Acelera el filtrado de nodos por propiedad

Para acelerar el filtrado por propiedades de los nodos, crea índices secundarios en propiedades. Por ejemplo, la siguiente consulta busca cuentas para un determinado sobrenombre. Sin un índice secundario, se analizan todos los nodos de Account para que coincidan los criterios de filtrado.

GRAPH FinGraph
MATCH (acct:Account)
WHERE acct.nick_name = "abcd"
RETURN acct.id;

Para acelerar la consulta, crea un índice secundario en la propiedad filtrada, como como se muestra en el siguiente ejemplo:

CREATE TABLE Account (
  id               INT64 NOT NULL,
  create_time      TIMESTAMP,
  is_blocked       BOOL,
  nick_name        STRING(MAX),
) PRIMARY KEY (id);

CREATE INDEX AccountByEmail
ON Account (nick_name);

Sugerencia: Usa índices filtrados por NULL para las propiedades dispersas. Para obtener más información, consulta Inhabilita la indexación de los valores NULL.

Acelera el recorrido del borde hacia adelante con el filtrado de las propiedades de borde

Cuando recorres una arista mientras filtras sus propiedades, puedes acelerar la consulta creando un índice secundario en las propiedades perimetrales y con la intercalación el índice en el nodo fuente.

Por ejemplo, la siguiente consulta encuentra cuentas que son propiedad de una persona determinada después de cierto tiempo:

GRAPH FinGraph
MATCH (person:Person)-[owns:Owns]->(acct:Account)
WHERE person.id = 1
  AND owns.create_time >= PARSE_TIMESTAMP("%c", "Thu Dec 25 07:30:00 2008")
RETURN acct.id;

De forma predeterminada, esta consulta lee todos los bordes de la persona especificada y, luego, filtra. las aristas que satisfacen la condición de create_time

En el siguiente ejemplo, se muestra cómo mejorar la eficiencia de las consultas creando un índice secundario en la referencia del nodo de origen perimetral (id) y la propiedad perimetral (create_time). Intercala el índice en la tabla de entrada del nodo fuente para y colocar el índice en el nodo fuente.

CREATE TABLE PersonOwnAccount (
  id               INT64 NOT NULL,
  account_id       INT64 NOT NULL,
  create_time      TIMESTAMP,
) PRIMARY KEY (id, account_id),
  INTERLEAVE IN PARENT Person ON DELETE CASCADE;

CREATE INDEX PersonOwnAccountByCreateTime
ON PersonOwnAccount (id, create_time)
INTERLEAVE IN Person;

Con este enfoque, la consulta puede encontrar de manera eficiente todas las aristas que satisfacen el en create_time.

Acelera el recorrido del borde inverso con el filtrado de las propiedades de las aristas

Cuando recorres un borde inverso mientras filtras sus propiedades, puedes acelerar la consulta creando un índice secundario con el nodo de destino y las propiedades del borde para filtrar.

La siguiente consulta de ejemplo realiza un recorrido de arista inversa con el filtrado activado Propiedades de borde:

GRAPH FinGraph
MATCH (acct:Account)<-[owns:Owns]-(person:Person)
WHERE acct.id = 1
  AND owns.create_time >= PARSE_TIMESTAMP("%c", "Thu Dec 25 07:30:00 2008") 
RETURN person.id;

Para acelerar esta consulta con un índice secundario, usa uno de los siguientes Opciones:

  • Crea un índice secundario en la referencia de nodo de destino perimetral (account_id) y la propiedad de borde (create_time), como se muestra en siguiente ejemplo:

    CREATE TABLE PersonOwnAccount (
      id               INT64 NOT NULL,
      account_id       INT64 NOT NULL,
      create_time      TIMESTAMP,
    ) PRIMARY KEY (id, account_id),
      INTERLEAVE IN PARENT Person ON DELETE CASCADE;
    
    CREATE INDEX PersonOwnAccountByCreateTime
    ON PersonOwnAccount (account_id, create_time);
    

    Este enfoque proporciona un mejor rendimiento porque los bordes inversos son ordenadas por account_id y create_time, lo que permite que el motor de consultas encontrar de manera eficiente aristas para account_id que satisfaga la condición de create_time Sin embargo, si diferentes patrones de consulta filtran propiedades, cada una podría requerir un índice independiente, agregar sobrecarga.

  • Crea un índice secundario en la referencia de nodo de destino perimetral (account_id) y almacena la propiedad perimetral (create_time) en una columna de almacenamiento, como se muestra en el siguiente ejemplo:

    CREATE TABLE PersonOwnAccount (
      id               INT64 NOT NULL,
      account_id       INT64 NOT NULL,
      create_time      TIMESTAMP,
    ) PRIMARY KEY (id, account_id),
      INTERLEAVE IN PARENT Person ON DELETE CASCADE;
    
    CREATE INDEX PersonOwnAccountByCreateTime
    ON PersonOwnAccount (account_id) STORING (create_time);
    

    Este enfoque puede almacenar varias propiedades: Sin embargo, la consulta debe leer todos los perímetros del nodo de destino y, luego, filtrar según las propiedades perimetrales.

Para combinar estos enfoques, sigue estos lineamientos:

  • Usa propiedades de borde en columnas de índice si se usan en para consultas críticas para el rendimiento.
  • Agrega las propiedades que se usan en consultas menos sensibles al rendimiento en las columnas de almacenamiento.

Tipos de aristas y nodos del modelo con etiquetas y propiedades

Los tipos de nodos y aristas suelen modelarse con etiquetas. Sin embargo, también puedes usar propiedades para modelar tipos. Considera un ejemplo en el que hay muchos tipos de cuentas diferentes, como BankAccount, InvestmentAccount y RetirementAccount. Puedes almacenar las cuentas en tablas de entrada separadas y modelarlas como etiquetas independientes o almacenar las cuentas en una sola y usar una propiedad para diferenciar entre los tipos.

Comienza el proceso de modelado mediante el modelado de los tipos con etiquetas. Considera usar propiedades en las siguientes situaciones.

Mejora la administración de esquemas

Si tu grafo tiene muchos tipos de nodos y aristas diferentes, administrar una entrada independiente tabla para cada uno puede volverse difícil. Para facilitar la administración de esquemas, el tipo como una propiedad.

Tipos de modelos en una propiedad para administrar tipos que cambian con frecuencia

Cuando modelas tipos como etiquetas, agregar o quitar tipos requiere cambios en el esquema. Si actualizas el esquema en un período breve, Spanner podría throttle el procesamiento de actualizaciones de esquema en cola. Para obtener más información, consulta Limita la frecuencia de las actualizaciones del esquema.

Si necesitas cambiar el esquema con frecuencia, te recomendamos modelar el escribe una propiedad para evitar limitaciones en la frecuencia del esquema actualizaciones.

Acelera las consultas

Los tipos de modelado con propiedades pueden acelerar las consultas cuando el nodo o el patrón perimetral hace referencia a varias etiquetas. La siguiente consulta de ejemplo busca todas las instancias de SavingsAccount y InvestmentAccount son propiedad de Person, suponiendo que se trata de una cuenta tipos se modelan con etiquetas:

GRAPH FinGraph
MATCH (:Person {id: 1})-[:Owns]->(acct:SavingsAccount|InvestmentAccount)
RETURN acct.id;

El patrón de nodo acct hace referencia a dos etiquetas. Si se trata de un consulta que es fundamental para el rendimiento, considera modelar Account con una propiedad. Esta puede proporcionar un mejor rendimiento de las consultas, como se muestra en la siguiente consulta ejemplo. Te recomendamos que compares ambas consultas.

GRAPH FinGraph
MATCH (:Person {id: 1})-[:Owns]->(acct:Account)
WHERE acct.type IN ("Savings", "Investment")
RETURN acct.id;

Almacena el tipo de clave en la clave del elemento del nodo para acelerar las consultas.

Para acelerar las consultas con el filtrado del tipo de nodo cuando se modela un tipo de nodo con una propiedad y el tipo no cambia a lo largo de la vida útil del nodo, sigue estos pasos:

  1. Incluye la propiedad como parte de la clave del elemento del nodo.
  2. Agrega el tipo de nodo a la tabla de entrada perimetral.
  3. Incluye el tipo de nodo en las claves de referencia perimetral.

En el siguiente ejemplo, se aplica esta optimización al nodo Account y al Borde de AccountTransferAccount.

CREATE TABLE Account (
  type             STRING(MAX) NOT NULL,
  id               INT64 NOT NULL,
  create_time      TIMESTAMP,
) PRIMARY KEY (type, id);

CREATE TABLE AccountTransferAccount (
  type             STRING(MAX) NOT NULL,
  id               INT64 NOT NULL,
  to_type          STRING(MAX) NOT NULL,
  to_id            INT64 NOT NULL,
  amount           FLOAT64,
  create_time      TIMESTAMP NOT NULL,
  order_number     STRING(MAX),
) PRIMARY KEY (type, id, to_type, to_id),
  INTERLEAVE IN PARENT Account ON DELETE CASCADE;

CREATE PROPERTY GRAPH FinGraph
  NODE TABLES (
    Account
  )
  EDGE TABLES (
    AccountTransferAccount
      SOURCE KEY (type, id) REFERENCES Account
      DESTINATION KEY (to_type, to_id) REFERENCES Account
  );

Configura el TTL en los nodos y las aristas

Spanner El tiempo de actividad (TTL) es un mecanismo que admite vencimiento y eliminación de datos después de un período específico. Se usa a menudo para datos que tienen una limitación de la vida útil o relevancia, como información de sesiones, cachés temporales o eventos de datos. En estos casos, el TTL ayuda a mantener el tamaño y el rendimiento de la base de datos.

En el siguiente ejemplo, se usa el TTL para borrar cuentas 90 días después de su cierre:

CREATE TABLE Account (
  id               INT64 NOT NULL,
  create_time      TIMESTAMP,
  close_time       TIMESTAMP,
) PRIMARY KEY (id),
  ROW DELETION POLICY (OLDER_THAN(close_time, INTERVAL 90 DAY));

Si la tabla de nodos tiene un TTL y una tabla perimetral intercalada, el la intercalación debe definirse con ON DELETE CASCADE Del mismo modo, si la tabla de nodos tiene un TTL y una tabla perimetral hace referencia a ella, a través de una clave externa, la clave externa debe definirse con ON DELETE CASCADE

En el siguiente ejemplo, AccountTransferAccount se almacena por un máximo de diez años. mientras la cuenta permanece activa. Cuando se borra una cuenta, la transferencia también se borrará el historial.

CREATE TABLE AccountTransferAccount (
  id               INT64 NOT NULL,
  to_id            INT64 NOT NULL,
  amount           FLOAT64,
  create_time      TIMESTAMP NOT NULL,
  order_number     STRING(MAX),
) PRIMARY KEY (id, to_id),
  INTERLEAVE IN PARENT Account ON DELETE CASCADE,
  ROW DELETION POLICY (OLDER_THAN(create_time, INTERVAL 3650 DAY));

Combina tablas de entrada de nodos y bordes

Puedes usar la misma tabla de entrada para definir más de un nodo y arista en tu .

En las siguientes tablas de ejemplo, los nodos Account tienen una clave compuesta (owner_id, account_id). Existe una definición de arista implícita, nodo Person con la clave (id) es propietario del nodo Account con clave compuesta (owner_id, account_id) cuando id es igual a owner_id.

CREATE TABLE Person (
  id INT64 NOT NULL,
) PRIMARY KEY (id);

-- Assume each account has exactly one owner.
CREATE TABLE Account (
  owner_id INT64 NOT NULL,   
  account_id INT64 NOT NULL,        
) PRIMARY KEY (owner_id, account_id);

En este caso, puedes usar la tabla de entrada Account para definir el Account y el borde PersonOwnAccount, como se muestra en el siguiente ejemplo de esquema. Para garantizar que todos los nombres de las tablas de elementos sean únicos, en el ejemplo se le da a la arista la definición de tabla del alias Owns.

CREATE PROPERTY GRAPH FinGraph
  NODE TABLES (
    Person,
    Account
  )
  EDGE TABLES (
    Account AS Owns
      SOURCE KEY (owner_id) REFERENCES Person
      DESTINATION KEY (owner_id, account_id) REFERENCES Account
  );

¿Qué sigue?