Ce document décrit les bonnes pratiques pour concevoir un schéma Spanner Graph, en se concentrant sur les requêtes efficaces, le parcours optimisé des arêtes et les techniques efficaces de gestion des données.
Pour en savoir plus sur la conception des schémas Spanner (et non des schémas Spanner Graph), consultez Bonnes pratiques relatives à la conception de schémas.
Choisir une conception de schéma
La conception de votre schéma a une incidence sur les performances du graphique. Les rubriques suivantes vous aideront à choisir une stratégie efficace.
Conceptions schématisées ou sans schéma
Une conception schématisée stocke la définition du graphique dans le schéma Spanner Graph, qui convient aux graphiques stables avec des modifications de définition peu fréquentes. Le schéma applique la définition du graphique, et les propriétés sont compatibles avec tous les types de données Spanner.
Une conception sans schéma déduit la définition du graphique à partir des données, ce qui offre plus de flexibilité sans nécessiter de modifications du schéma. Les libellés et les propriétés dynamiques ne sont pas appliqués par défaut. Les propriétés doivent être des valeurs JSON valides.
Le tableau suivant récapitule les principales différences entre la gestion des données avec et sans schéma. Pensez également à vos requêtes de graphiques pour vous aider à choisir le type de schéma à utiliser.
Fonctionnalité | Gestion des données schématisées | Gestion des données sans schéma |
---|---|---|
Stocker la définition du graphique | La définition du graphique est stockée dans le schéma Spanner Graph. | La définition du graphique est évidente à partir des données. Toutefois, Spanner Graph n'inspecte pas les données pour déduire la définition. |
Mettre à jour la définition du graphique | Nécessite une modification du schéma Spanner Graph. Convient lorsque la définition est bien définie et change rarement. | Aucune modification du schéma Spanner Graph n'est nécessaire. |
Appliquer la définition du graphique | Un schéma de graphique de propriétés applique les types de nœuds autorisés pour une arête. Il applique également les propriétés et les types de propriétés autorisés d'un type de nœud ou d'arête de graphique. | Non appliqué par défaut. Vous pouvez utiliser des contraintes de vérification pour garantir l'intégrité des données de libellés et de propriétés. |
Types de données de propriété | Compatible avec tous les types de données Spanner, par exemple timestamp . |
Les propriétés dynamiques doivent être une valeur JSON valide. |
Choisir une conception de schéma basée sur des requêtes graphiques
Les conceptions schématisées et sans schéma offrent souvent des performances comparables. Toutefois, lorsque les requêtes utilisent des motifs de chemin quantifiés qui couvrent plusieurs types de nœuds ou d'arêtes, une conception sans schéma offre de meilleures performances.
Le modèle de données sous-jacent est l'une des principales raisons de cette situation. Une conception sans schéma stocke toutes les données dans des tables de nœuds et d'arêtes uniques, ce qui DYNAMIC LABEL
applique.
Les requêtes qui traversent plusieurs types s'exécutent avec un minimum d'analyses de tables.
En revanche, les conceptions schématisées utilisent généralement des tables distinctes pour chaque type de nœud et d'arête. Par conséquent, les requêtes couvrant plusieurs types doivent analyser et combiner les données de toutes les tables correspondantes.
Voici des exemples de requêtes qui fonctionnent bien avec les conceptions sans schéma, ainsi qu'un exemple de requête qui fonctionne bien avec les deux conceptions :
Conception sans schéma
Les requêtes suivantes sont plus performantes avec une conception sans schéma, car elles utilisent des modèles de chemin quantifiés qui peuvent correspondre à plusieurs types de nœuds et d'arêtes :
Le modèle de chemin quantifié de cette requête utilise plusieurs types d'arêtes (
Transfer
ouWithdraw
) et ne spécifie pas de types de nœuds intermédiaires pour les chemins de plus d'un saut.GRAPH FinGraph MATCH p = (:Account {id:1})-[:Transfer|Withdraw]->{1,3}(:Account) RETURN TO_JSON(p) AS p;
Le modèle de chemin quantifié de cette requête trouve des chemins d'un à trois sauts entre les nœuds
Person
etAccount
, en utilisant plusieurs types d'arêtes (Owns
ouTransfers
), sans spécifier de types de nœuds intermédiaires pour les chemins plus longs. Cela permet aux chemins de traverser des nœuds intermédiaires de différents types. Par exemple,(:Person)-[:Owns]->(:Account)-[:Transfers]->(:Account)
.GRAPH FinGraph MATCH p = (:Person {id:1})-[:Owns|Transfers]->{1,3}(:Account) RETURN TO_JSON(p) AS p;
Le modèle de chemin quantifié de cette requête trouve des chemins d'un à trois sauts entre les nœuds
Person
etAccount
, sans spécifier de libellés d'arête. Comme la requête précédente, elle permet aux chemins de traverser des nœuds intermédiaires de différents types.GRAPH FinGraph MATCH p = (:Person {id:1})-[]->{1,3}(:Account) RETURN TO_JSON(p) AS p;
Cette requête recherche des chemins d'un à trois sauts entre les nœuds
Account
à l'aide d'arêtes de typeOwns
dans n'importe quelle direction (-[:Owns]-
). Étant donné que les chemins peuvent traverser des arêtes dans les deux sens et que les nœuds intermédiaires ne sont pas spécifiés, un chemin à deux sauts peut passer par des nœuds de différents types. Par exemple,(:Account)-[:Owns]-(:Person)-[:Owns]-(:Account)
.GRAPH FinGraph MATCH p = (:Account {id:1})-[:Owns]-{1,3}(:Account) RETURN TO_JSON(p) AS p;
Les deux conceptions
La requête suivante est comparable avec les conceptions schématisées et sans schéma. Son chemin quantifié, (:Account)-[:Transfer]->{1,3}(:Account)
, implique un type de nœud, Account
, et un type d'arête, Transfer
. Étant donné que le chemin n'implique qu'un seul type de nœud et un seul type d'arête, les performances sont comparables pour les deux conceptions. Même si les nœuds intermédiaires ne sont pas explicitement étiquetés, le modèle les contraint à être des nœuds Account
. Le nœud Person
apparaît en dehors de ce chemin quantifié.
GRAPH FinGraph
MATCH p = (:Person {id:1})-[:Owns]->(:Account)-[:Transfer]->{1,3}(:Account)
RETURN TO_JSON(p) AS p;
Optimiser les performances du schéma Spanner Graph
Une fois que vous avez choisi d'utiliser un schéma Spanner Graph schématisé ou non schématisé, vous pouvez optimiser ses performances de différentes manières :
Optimiser la traversée des arêtes
La traversée d'arêtes est le processus de navigation dans un graphique en suivant ses arêtes, en commençant par un nœud particulier et en se déplaçant le long des arêtes connectées pour atteindre d'autres nœuds. Le schéma définit la direction de l'arête. Le parcours des arêtes est une opération fondamentale dans Spanner Graph. Améliorer son efficacité peut donc améliorer considérablement les performances de votre application.
Vous pouvez parcourir une arête dans deux directions :
- La traversée d'arêtes vers l'avant suit les arêtes sortantes du nœud source.
- La traversée d'arêtes inversée suit les arêtes entrantes du nœud de destination.
Exemples de requêtes de traversée d'arêtes avant et arrière
L'exemple de requête suivant effectue une traversée d'arêtes vers l'avant des arêtes Owns
pour une personne donnée :
GRAPH FinGraph
MATCH (person:Person {id: 1})-[owns:Owns]->(accnt:Account)
RETURN accnt.id;
L'exemple de requête suivant effectue une traversée d'arêtes inversée des arêtes Owns
pour un compte donné :
GRAPH FinGraph
MATCH (accnt:Account {id: 1})<-[owns:Owns]-(person:Person)
RETURN person.name;
Optimiser la traversée de bord avant
Pour améliorer les performances de traversée des arêtes sortantes, optimisez la traversée de la source vers l'arête et de l'arête vers la destination.
Pour optimiser la traversée de la source à la périphérie, entrelacez la table d'entrée de périphérie dans la table d'entrée de nœud source à l'aide de la clause
INTERLEAVE IN PARENT
. L'entrelacement est une technique d'optimisation du stockage dans Spanner qui permet de colocaliser les lignes d'une table enfant avec les lignes parentes correspondantes dans le stockage. Pour en savoir plus sur l'entrelacement, consultez Présentation des schémas.Pour optimiser la traversée des arêtes vers la destination, créez une contrainte de clé étrangère entre l'arête et le nœud
de destination. Cela applique la contrainte de bord à destination, ce qui peut améliorer les performances en éliminant les analyses de tables de destination. Si les clés étrangères appliquées entraînent des goulots d'étranglement des performances d'écriture (par exemple, lors de la mise à jour des nœuds hub), utilisez plutôt une clé étrangère informationnelle.
Les exemples suivants montrent comment utiliser l'entrelacement avec une contrainte de clé étrangère appliquée et informative.
Clé étrangère appliquée
Dans cet exemple de tableau Edge, PersonOwnAccount
effectue les opérations suivantes :
S'entrelace dans la table de nœuds source
Person
.Crée une clé étrangère appliquée à la table de nœuds de destination
Account
.
CREATE TABLE Person (
id INT64 NOT NULL,
name STRING(MAX),
) PRIMARY KEY (id);
CREATE TABLE Account (
id INT64 NOT NULL,
create_time TIMESTAMP,
close_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;
Clé étrangère informationnelle
Dans cet exemple de tableau Edge, PersonOwnAccount
effectue les opérations suivantes :
S'entrelace dans la table de nœuds source
Person
.Crée une clé étrangère informative pour la table de nœuds de destination
Account
.
CREATE TABLE Person (
id INT64 NOT NULL,
name STRING(MAX),
) PRIMARY KEY (id);
CREATE TABLE Account (
id INT64 NOT NULL,
create_time TIMESTAMP,
close_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) NOT ENFORCED
) PRIMARY KEY (id, account_id),
INTERLEAVE IN PARENT Person ON DELETE CASCADE;
Optimiser la traversée des arêtes inversées
Optimisez le parcours des arêtes inversées, sauf si vos requêtes n'utilisent que le parcours des arêtes directes, car les requêtes impliquant le parcours des arêtes inversées ou bidirectionnelles sont courantes.
Pour optimiser le parcours des arêtes inversées, vous pouvez procéder comme suit :
Créez un index secondaire sur la table d'arêtes.
Entrelacez l'index dans la table d'entrée du nœud de destination pour colocaliser les arêtes avec les nœuds de destination.
Stockez les propriétés des arêtes dans l'index.
Cet exemple montre un index secondaire permettant d'optimiser la traversée des arêtes inversées pour la table d'arêtes PersonOwnAccount
:
La clause
INTERLEAVE IN
place les données d'index avec la table de nœuds de destinationAccount
.La clause
STORING
stocke les propriétés des arêtes dans l'index.
Pour en savoir plus sur l'entrelacement des index, consultez Index et entrelacement.
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 AccountOwnedByPerson
ON PersonOwnAccount (account_id)
STORING (create_time),
INTERLEAVE IN Account;
Utiliser des index secondaires pour filtrer les propriétés
Un index secondaire permet de rechercher efficacement des nœuds et des arêtes en fonction de valeurs de propriétés spécifiques. L'utilisation d'un index permet d'éviter une analyse complète de la table et est particulièrement utile pour les grands graphiques.
Accélérer le filtrage des nœuds par propriété
La requête suivante permet de trouver des comptes pour un pseudo spécifié. Comme il n'utilise pas d'index secondaire, tous les nœuds Account
doivent être analysés pour trouver les résultats correspondants :
GRAPH FinGraph
MATCH (acct:Account)
WHERE acct.nick_name = "abcd"
RETURN acct.id;
Créez un index secondaire sur la propriété filtrée dans votre schéma pour accélérer le processus de filtrage :
CREATE TABLE Account (
id INT64 NOT NULL,
create_time TIMESTAMP,
is_blocked BOOL,
nick_name STRING(MAX),
) PRIMARY KEY (id);
CREATE INDEX AccountByNickName
ON Account (nick_name);
Accélérer le filtrage des arêtes par propriété
Vous pouvez utiliser un index secondaire pour améliorer les performances du filtrage des arêtes en fonction des valeurs de propriétés.
Parcours de bord avant
Sans index secondaire, cette requête doit analyser tous les nœuds d'une personne pour trouver ceux qui correspondent au filtre create_time
:
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;
Le code suivant améliore l'efficacité des requêtes en créant un index secondaire sur la référence du nœud source d'arête (id
) et la propriété d'arête (create_time
). La requête définit également l'index comme enfant entrelacé de la table d'entrée du nœud source, ce qui le place au même endroit que le nœud source.
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;
Traversée d'arêtes inversée
Sans index secondaire, la requête de traversée d'arête inverse suivante doit lire toutes les arêtes avant de pouvoir trouver la personne qui possède le compte spécifié après le create_time
spécifié :
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;
Le code suivant améliore l'efficacité des requêtes en créant un index secondaire sur la référence du nœud de destination de l'arête (account_id
) et la propriété de l'arête (create_time
). La requête définit également l'index comme enfant entrelacé de la table du nœud de destination, ce qui le place au même endroit que le nœud de destination.
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 AccountOwnedByPersonByCreateTime
ON PersonOwnAccount (account_id, create_time),
INTERLEAVE IN Account;
Éviter les arêtes isolées
Une arête qui relie zéro ou un nœud (arête isolée) peut compromettre l'efficacité des requêtes Spanner Graph et l'intégrité de la structure du graphique. Une arête isolée peut se produire si vous supprimez un nœud sans supprimer les arêtes associées. Une arête isolée peut également se produire si vous créez une arête, mais que son nœud source ou de destination n'existe pas. Pour éviter les arêtes isolées, intégrez les éléments suivants dans votre schéma Spanner Graph :
- Utilisez des contraintes référentielles.
- Facultatif : Utilisez la clause
ON DELETE CASCADE
lorsque vous supprimez un nœud dont les arêtes sont toujours associées. Si vous n'utilisez pasON DELETE CASCADE
, les tentatives de suppression d'un nœud sans supprimer les arêtes correspondantes échouent.
Utiliser des contraintes référentielles
Vous pouvez utiliser l'entrelacement et les clés étrangères forcées sur les deux points de terminaison pour éviter les arêtes orphelines en procédant comme suit :
Entrelacez la table d'entrée des arêtes dans la table d'entrée des nœuds sources pour vous assurer que le nœud source d'une arête existe toujours.
Créez une contrainte de clé étrangère appliquée sur les arêtes pour vous assurer que le nœud de destination d'une arête existe toujours. Bien que les clés étrangères appliquées empêchent les arêtes orphelines, elles rendent l'insertion et la suppression d'arêtes plus coûteuses.
L'exemple suivant utilise une clé étrangère appliquée et entrelace la table d'entrée des arêtes dans la table d'entrée des nœuds sources à l'aide de la clause INTERLEAVE IN PARENT
. L'utilisation conjointe d'une clé étrangère appliquée et de l'entrelacement peut également aider à optimiser le parcours des arêtes sortantes.
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;
Supprimer des arêtes avec ON DELETE CASCADE
Lorsque vous utilisez l'entrelacement ou une clé étrangère forcée pour éviter les arêtes orphelines, utilisez la clause ON DELETE CASCADE
dans votre schéma Spanner Graph pour supprimer les arêtes associées d'un nœud dans la même transaction que celle qui supprime le nœud.
Pour en savoir plus, consultez Suppression en cascade pour les tables entrelacées et Actions de clé étrangère.
Suppression en cascade pour les arêtes reliant différents types de nœuds
Les exemples suivants montrent comment utiliser ON DELETE CASCADE
dans votre schéma Spanner Graph pour supprimer les arêtes orphelines lorsque vous supprimez un nœud source ou de destination. Dans les deux cas, le type du nœud supprimé et celui du nœud auquel il est connecté par un bord sont différents.
Nœud source
Utilisez l'entrelacement pour supprimer les arêtes orphelines lorsque le nœud source est supprimé. L'exemple suivant montre comment utiliser l'entrelacement pour supprimer les arêtes sortantes lorsque le nœud source (Person
) est supprimé. Pour en savoir plus, consultez Créer des tables entrelacées.
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
Nœud de destination
Utilisez une contrainte de clé étrangère pour supprimer les arêtes orphelines lorsque le nœud de destination est supprimé. L'exemple suivant montre comment utiliser une clé étrangère avec ON
DELETE CASCADE
dans une table d'arêtes pour supprimer les arêtes entrantes lorsque le nœud de destination (Account
) est supprimé :
CONSTRAINT FK_Account FOREIGN KEY(account_id)
REFERENCES Account(id) ON DELETE CASCADE
Suppression en cascade pour les arêtes connectant le même type de nœuds
Lorsque les nœuds source et de destination d'un bord sont du même type et que le bord est entrelacé dans le nœud source, vous pouvez définir ON DELETE CASCADE
pour le nœud source ou de destination, mais pas les deux.
Pour éviter les arêtes isolées dans ces scénarios, n'entrelacez pas les données dans la table d'entrée du nœud source. Créez plutôt deux clés étrangères appliquées sur les références de nœuds source et de destination.
L'exemple suivant utilise AccountTransferAccount
comme table d'entrée des arêtes. Il définit deux clés étrangères, une sur chaque nœud d'extrémité de l'arête de transfert, toutes deux avec l'action ON DELETE CASCADE
.
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);
Configurer la valeur TTL (Time To Live) sur les nœuds et les arêtes
Le TTL vous permet de faire expirer et de supprimer des données après une période spécifiée. Vous pouvez utiliser le TTL dans votre schéma pour maintenir la taille et les performances de la base de données en supprimant les données dont la durée de vie ou la pertinence sont limitées. Par exemple, vous pouvez le configurer pour qu'il supprime les informations de session, les caches temporaires ou les journaux d'événements.
L'exemple suivant utilise le TTL pour supprimer les comptes 90 jours après leur clôture :
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));
Lorsque vous définissez une règle TTL sur une table de nœuds, vous devez configurer la façon dont les arêtes associées sont gérées pour éviter les arêtes orphelines involontaires :
Pour les tables d'arêtes entrelacées : si une table d'arêtes est entrelacée dans la table de nœuds, vous pouvez définir la relation d'entrelacement avec
ON DELETE CASCADE
. Cela garantit que lorsque le TTL supprime un nœud, ses arêtes entrelacées associées sont également supprimées.Pour les tables d'arêtes avec des clés étrangères : si une table d'arêtes référence la table de nœuds avec une clé étrangère, vous avez deux options :
- Pour supprimer automatiquement les arêtes lorsque le nœud référencé est supprimé par le TTL, utilisez
ON DELETE CASCADE
sur la clé étrangère. Cela permet de préserver l'intégrité référentielle. - Pour autoriser les arêtes à rester après la suppression du nœud référencé (création d'une arête orpheline), définissez la clé étrangère comme clé étrangère informationnelle.
- Pour supprimer automatiquement les arêtes lorsque le nœud référencé est supprimé par le TTL, utilisez
Dans l'exemple suivant, la table d'arêtes AccountTransferAccount
est soumise à deux règles de suppression des données :
- Une règle TTL supprime les enregistrements de transfert datant de plus de 10 ans.
- La clause
ON DELETE CASCADE
supprime tous les enregistrements de transfert associés à une source lorsque ce compte est supprimé.
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));
Fusionner les tables d'entrée de nœuds et d'arêtes
Pour optimiser votre schéma, définissez un nœud et ses arêtes entrantes ou sortantes dans une même table. Cette approche présente les avantages suivants :
Moins de tables : réduit le nombre de tables dans votre schéma, ce qui simplifie la gestion des données.
Amélioration des performances des requêtes : élimine le parcours qui utilise des jointures vers une table d'arêtes distincte.
Cette technique fonctionne bien lorsque la clé primaire d'une table définit également une relation avec une autre table. Par exemple, si la table Account
possède une clé primaire composite (owner_id, account_id)
, la partie owner_id
peut être une clé étrangère qui fait référence à la table Person
. Cette structure permet à la table Account
de représenter à la fois le nœud Account
et l'arête entrante du nœud Person
.
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);
Vous pouvez utiliser la table Account
pour définir à la fois le nœud Account
et son arête Owns
entrante. Ceci est illustré dans l'instruction CREATE PROPERTY GRAPH
suivante. Dans la clause EDGE TABLES
, vous attribuez l'alias Owns
à la table Account
. En effet, chaque élément du schéma de graphique doit avoir un nom unique.
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
);
Étapes suivantes
- Créer, mettre à jour ou supprimer un schéma Spanner Graph.
- Insérer, mettre à jour ou supprimer des données Spanner Graph