Sélectionner vos préférences de cookies

Nous utilisons des cookies essentiels et des outils similaires qui sont nécessaires au fonctionnement de notre site et à la fourniture de nos services. Nous utilisons des cookies de performance pour collecter des statistiques anonymes afin de comprendre comment les clients utilisent notre site et d’apporter des améliorations. Les cookies essentiels ne peuvent pas être désactivés, mais vous pouvez cliquer sur « Personnaliser » ou « Refuser » pour refuser les cookies de performance.

Si vous êtes d’accord, AWS et les tiers approuvés utiliseront également des cookies pour fournir des fonctionnalités utiles au site, mémoriser vos préférences et afficher du contenu pertinent, y compris des publicités pertinentes. Pour accepter ou refuser tous les cookies non essentiels, cliquez sur « Accepter » ou « Refuser ». Pour effectuer des choix plus détaillés, cliquez sur « Personnaliser ».

Réalisation d'upserts efficaces avec les étapes Gremlin mergeV() et mergeE()

Mode de mise au point
Réalisation d'upserts efficaces avec les étapes Gremlin mergeV() et mergeE() - Amazon Neptune

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

Une insertion conditionnelle (également appelée « upsert ») réutilise un sommet ou une arête qui existe déjà, ou crée l'objet nécessaire dans le cas contraire. Des upserts efficaces peuvent faire une différence significative dans les performances des requêtes Gremlin.

Les upserts vous permettent d'écrire des opérations d'insertion idempotentes : quel que soit le nombre de fois que vous exécutez cette opération, le résultat global est le même. Cela est utile dans les scénarios d'écriture hautement simultanés où les modifications simultanées apportées à la même partie du graphe peuvent forcer une ou plusieurs transactions à revenir en arrière avec une exception ConcurrentModificationException, nécessitant ainsi de nouvelles tentatives.

Par exemple, la requête suivante insère un sommet en utilisant l'élément Map fourni pour essayer d'abord de trouver un sommet avec le T.id "v-1". Si ce sommet est trouvé, il est renvoyé. Dans le cas contraire, un sommet contenant cet id et cette propriété est créé par le biais de la clause onCreate.

g.mergeV([(id):'v-1']). option(onCreate, [(label): 'PERSON', 'email': 'person-1@example.org'])

Exécution d'upserts par lots pour améliorer le débit

Pour les scénarios d'écriture à haut débit, vous pouvez enchaîner les étapes mergeV() et mergeE() pour effectuer l'upsert en bloc des sommets et des arêtes. Le traitement par lots réduit la charge transactionnelle liée à l'insertion par upsert d'un grand nombre de sommets et d'arêtes. Vous pouvez ainsi améliorer davantage le débit en augmentant les demandes d'upserts par lots en parallèle à l'aide de plusieurs clients.

En règle générale, nous recommandons d'insérer par upsert environ 200 enregistrements par demande par lots. Un enregistrement correspond à une étiquette ou propriété individuelle de sommet ou d'arête. Par exemple, un sommet doté d'une seule étiquette et de quatre propriétés génère cinq enregistrements. Une arête dotée d'une étiquette et d'une seule propriété génère deux enregistrements. Si vous souhaitez insérer par upsert des lots de sommets, chacun avec une seule étiquette et quatre propriétés, vous devez commencer par une taille de lot de 40, car 200 / (1 + 4) = 40.

Vous pouvez tester différentes tailles de lots. 200 enregistrements par lot constituent un bon point de départ, mais la taille de lot idéale peut être supérieure ou inférieure en fonction de votre charge de travail. Notez toutefois que Neptune peut limiter le nombre total d'étapes Gremlin par demande. Cette limite n'est pas documentée, mais par mesure de sécurité, essayez de faire en sorte que les demandes ne contiennent pas plus de 1 500 étapes Gremlin. Neptune peut rejeter des demandes en bloc volumineuses comportant plus de 1 500 étapes.

Pour augmenter le débit, vous pouvez insérer par upsert des lots en parallèle à l'aide de plusieurs clients (voir Création d'écritures Gremlin multithreads efficaces). Le nombre de clients doit être identique au nombre de threads de travail sur votre instance Neptune Writer, qui est généralement 2 fois le nombre de v CPUs sur le serveur. Par exemple, une r5.8xlarge instance possède 32 V CPUs et 64 threads de travail. Pour les scénarios d'écriture à haut débit utilisant une instance r5.8xlarge, vous devez utiliser 64 clients écrivant des upserts par lots sur Neptune en parallèle.

Chaque client doit soumettre une demande par lots et attendre qu'elle soit terminée avant de soumettre une autre demande. Bien que les différents clients fonctionnent en parallèle, chacun d'eux soumet des demandes en série. Cela garantit que le serveur reçoit un flux constant de demandes qui occupent tous les threads de travail sans encombrer la file d'attente des demandes côté serveur (voir Dimensionnement des instances de base de données dans un cluster de bases de données Neptune).

Essayer d'éviter les étapes qui génèrent plusieurs traverseurs

Lorsqu'une étape Gremlin s'exécute, elle utilise un traverseur entrant et émet un ou plusieurs traverseurs de sortie. Le nombre de traverseurs émis par une étape détermine le nombre de fois que l'étape suivante sera exécutée.

Généralement, lorsque vous effectuez des opérations par lots, vous souhaitez que chaque opération, telle que l'upsert du sommet A, soit exécutée une seule fois, de sorte que la séquence des opérations ressemble à ceci : upsert du sommet A, puis upsert du sommet B, puis upsert du sommet C, etc. Tant qu'une étape ne crée ou ne modifie qu'un seul élément, elle n'émet qu'un seul traverseur, et les étapes représentant l'opération suivante ne sont exécutées qu'une seule fois. Si, en revanche, une opération crée ou modifie plusieurs éléments, elle émet plusieurs traverseurs, ce qui entraîne l'exécution des étapes suivantes plusieurs fois, une fois par traverseur émis. Cela peut obliger la base de données à effectuer des tâches supplémentaires inutiles et, dans certains cas, à créer des sommets, des arêtes ou des valeurs de propriétés supplémentaires superflus.

La requête g.V().addV() est un bon exemple de cas où la situation peut dégénérer. Cette requête simple ajoute un sommet pour chaque sommet du graphe, car V() émet un traverseur pour chaque sommet du graphe et chacun de ces traverseurs déclenche un appel à addV().

Consultez Combinaison d'upserts et d'insertions pour découvrir comment gérer les opérations qui peuvent émettre plusieurs traverseurs.

Insertion de sommets par upsert

L'étape mergeV() est spécialement conçue pour l'upsert de sommets. Elle utilise comme argument un objet Map qui représente les éléments correspondant aux sommets existants dans le graphe. Si aucun élément n'est trouvé, elle utilise cet objet Map pour créer un sommet. Cette étape vous permet également de modifier le comportement en cas de création ou de correspondance. Le modulateur option() peut alors être associé à des jetons Merge.onCreate et Merge.onMatch pour contrôler ces comportements respectifs. Consultez la documentation de TinkerPop référence pour plus d'informations sur l'utilisation de cette étape.

Vous pouvez utiliser un ID de sommet pour déterminer si un sommet spécifique existe. Il s'agit de l'approche préférée, car Neptune optimise les upserts pour les cas d'utilisation très concurrents. IDs Par exemple, la requête suivante crée un sommet avec un ID de sommet donné s'il n'existe pas déjà, ou le réutilise s'il existe déjà :

g.mergeV([(T.id): 'v-1']). option(onCreate, [(T.label): 'PERSON', email: 'person-1@example.org', age: 21]). option(onMatch, [age: 22]). id()

Notez que cette requête se termine par une étape id(). Bien que cela ne soit pas strictement nécessaire pour réaliser l'upsert du sommet, une étape id() à la fin d'une requête d'upsert garantit que le serveur ne sérialise pas toutes les propriétés du sommet vers le client, ce qui contribue à réduire le coût de verrouillage de la requête.

Vous pouvez également utiliser une propriété de sommet pour identifier un sommet :

g.mergeV([email: 'person-1@example.org']). option(onCreate, [(T.label): 'PERSON', age: 21]). option(onMatch, [age: 22]). id()

Si possible, utilisez les sommets fournis par l'utilisateur IDs pour créer des sommets, et utilisez-les IDs pour déterminer si un sommet existe lors d'une opération de remontée. Cela permet à Neptune d'optimiser les upserts. Un upsert basé sur un ID peut être nettement plus efficace qu'un upsert basé sur des propriétés lorsque des modifications simultanées sont courantes.

Enchaînement d'upserts de sommets

Vous pouvez enchaîner des upserts de sommets pour les insérer dans un lot :

g.V('v-1') .fold() .coalesce(unfold(), addV('Person').property(id, 'v-1') .property('email', 'person-1@example.org')) .V('v-2') .fold() .coalesce(unfold(), addV('Person').property(id, 'v-2') .property('email', 'person-2@example.org')) .V('v-3') .fold() .coalesce(unfold(), addV('Person').property(id, 'v-3') .property('email', 'person-3@example.org')) .id()

Vous pouvez également utiliser cette syntaxe mergeV() :

g.mergeV([(T.id): 'v-1', (T.label): 'PERSON', email: 'person-1@example.org']). mergeV([(T.id): 'v-2', (T.label): 'PERSON', email: 'person-2@example.org']). mergeV([(T.id): 'v-3', (T.label): 'PERSON', email: 'person-3@example.org'])

Cependant, comme cette forme de requête inclut des éléments dans les critères de recherche qui sont superflus par rapport à la recherche de base par id, elle n'est pas aussi efficace que la requête précédente.

Exécution d'upserts d'arêtes

L'étape mergeE() est spécialement conçue pour l'upsert d'arêtes. Elle utilise comme argument un objet Map qui représente les éléments correspondant aux arêtes existantes dans le graphe. Si aucun élément n'est trouvé, elle utilise cet objet Map pour créer une arête. Cette étape vous permet également de modifier le comportement en cas de création ou de correspondance. Le modulateur option() peut alors être associé à des jetons Merge.onCreate et Merge.onMatch pour contrôler ces comportements respectifs. Consultez la documentation de TinkerPop référence pour plus d'informations sur l'utilisation de cette étape.

Vous pouvez utiliser une arête IDs pour insérer des arêtes de la même manière que vous insérez des sommets à l'aide d'un sommet personnalisé. IDs Là aussi, il s'agit de l'approche préférée, car elle permet à Neptune d'optimiser la requête. Par exemple, la requête suivante crée une arête en fonction de son ID d'arête si elle n'existe pas déjà, ou la réutilise si elle existe déjà. La requête utilise également IDs les Direction.to sommets Direction.from et si elle doit créer une nouvelle arête :

g.mergeE([(T.id): 'e-1']). option(onCreate, [(from): 'v-1', (to): 'v-2', weight: 1.0]). option(onMatch, [weight: 0.5]). id()

Notez que cette requête se termine par une étape id(). Bien que cela ne soit pas strictement nécessaire pour réaliser l'upsert de l'arête, une étape id() à la fin d'une requête d'upsert garantit que le serveur ne sérialise pas toutes les propriétés de l'arête vers le client, ce qui contribue à réduire le coût de verrouillage de la requête.

De nombreuses applications utilisent un sommet personnalisé IDs, mais laissent à Neptune le soin de générer l'arête. IDs Si vous ne connaissez pas l'ID d'une arête, mais que vous connaissez le to sommet from et IDs, vous pouvez utiliser ce type de requête pour modifier une arête :

g.mergeE([(from): 'v-1', (to): 'v-2', (T.label): 'KNOWS']). id()

Tous les sommets référencés par mergeE() doivent exister pour que l'étape crée l'arête.

Enchaînement d'upserts d'arêtes

Comme pour les upserts de sommets, il est simple d'enchaîner les étapes mergeE() pour les demandes en bloc :

g.mergeE([(from): 'v-1', (to): 'v-2', (T.label): 'KNOWS']). mergeE([(from): 'v-2', (to): 'v-3', (T.label): 'KNOWS']). mergeE([(from): 'v-3', (to): 'v-4', (T.label): 'KNOWS']). id()

Combinaison d'upserts de sommets et d'arêtes

Parfois, il peut être utile d'insérer par upsert à la fois les sommets et les arêtes qui les relient. Vous pouvez combiner les exemples de lots présentés ici. L'exemple suivant insère par upsert trois sommets et deux arêtes :

g.mergeV([(id):'v-1']). option(onCreate, [(label): 'PERSON', 'email': 'person-1@example.org']). mergeV([(id):'v-2']). option(onCreate, [(label): 'PERSON', 'email': 'person-2@example.org']). mergeV([(id):'v-3']). option(onCreate, [(label): 'PERSON', 'email': 'person-3@example.org']). mergeE([(from): 'v-1', (to): 'v-2', (T.label): 'KNOWS']). mergeE([(from): 'v-2', (to): 'v-3', (T.label): 'KNOWS']). id()

Combinaison d'upserts et d'insertions

Parfois, il peut être utile d'insérer par upsert à la fois les sommets et les arêtes qui les relient. Vous pouvez combiner les exemples de lots présentés ici. L'exemple suivant insère par upsert trois sommets et deux arêtes :

Les upserts traitent généralement un élément à la fois. Si vous vous en tenez aux modèles d'upsert présentés ici, chaque opération d'upsert émet un seul traverseur, ce qui entraîne l'exécution de l'opération suivante une seule fois.

Cependant, il peut arriver que vous souhaitiez combiner des upserts avec des insertions. Cela peut notamment être le cas si vous utilisez des arêtes pour représenter des instances d'actions ou d'événements. Une demande peut utiliser des upserts pour s'assurer que tous les sommets nécessaires existent, puis utiliser des insertions pour ajouter des arêtes. Avec les demandes de ce type, soyez attentif au nombre potentiel de traverseurs émis par chaque opération.

Prenons l'exemple suivant, qui combine des upserts et des insertions pour ajouter des arêtes représentant des événements dans le graphe :

// Fully optimized, but inserts too many edges g.mergeV([(id):'v-1']). option(onCreate, [(label): 'PERSON', 'email': 'person-1@example.org']). mergeV([(id):'v-2']). option(onCreate, [(label): 'PERSON', 'email': 'person-2@example.org']). mergeV([(id):'v-3']). option(onCreate, [(label): 'PERSON', 'email': 'person-3@example.org']). mergeV([(T.id): 'c-1', (T.label): 'CITY', name: 'city-1']). V('p-1', 'p-2'). addE('FOLLOWED').to(V('p-1')). V('p-1', 'p-2', 'p-3'). addE('VISITED').to(V('c-1')). id()

La requête doit insérer cinq arêtes : deux arêtes SUIVIES et trois arêtes VISITÉES. Cependant, la requête telle qu'elle est écrite insère huit arêtes : deux arêtes SUIVIES et six arêtes VISITÉES. Cela est dû au fait que l'opération qui insère les deux arêtes suivies émet deux traverseurs, ce qui entraîne l'exécution de l'opération suivante d'insertion de trois arêtes deux fois.

La solution consiste à ajouter une étape fold() après chaque opération susceptible d'émettre plusieurs traverseurs :

g.mergeV([(T.id): 'v-1', (T.label): 'PERSON', email: 'person-1@example.org']). mergeV([(T.id): 'v-2', (T.label): 'PERSON', email: 'person-2@example.org']). mergeV([(T.id): 'v-3', (T.label): 'PERSON', email: 'person-3@example.org']). mergeV([(T.id): 'c-1', (T.label): 'CITY', name: 'city-1']). V('p-1', 'p-2'). addE('FOLLOWED'). to(V('p-1')). fold(). V('p-1', 'p-2', 'p-3'). addE('VISITED'). to(V('c-1')). id()

Nous avons inséré ici une étape fold() après l'opération qui insère les arêtes SUIVIES. Il en résulte un seul traverseur, et l'opération suivante n'est donc exécutée qu'une seule fois.

L'inconvénient de cette approche est que la requête n'est plus entièrement optimisée, car fold() n'est pas optimisé. L'opération d'insertion qui suit fold() ne sera maintenant pas optimisée non plus.

Si vous devez utiliser fold() pour réduire le nombre de traverseurs lors des étapes suivantes, essayez d'organiser les opérations de manière à ce que les moins coûteuses occupent la partie non optimisée de la requête.

Configuration de la cardinalité

La cardinalité par défaut pour les propriétés des sommets dans Neptune est définie, ce qui signifie que lors de l'utilisation de mergeV (), les valeurs fournies sur la carte recevront toutes cette cardinalité. Pour utiliser la cardinalité unique, vous devez être explicite dans son utilisation. À partir de la TinkerPop version 3.7.0, une nouvelle syntaxe permet de fournir la cardinalité dans le cadre de la carte, comme indiqué dans l'exemple suivant :

g.mergeV([(T.id): '1234']). option(onMatch, ['age': single(20), 'name': single('alice'), 'city': set('miami')])

Vous pouvez également définir la cardinalité par défaut pour cela option comme suit :

// age and name are set to single cardinality by default g.mergeV([(T.id): '1234']). option(onMatch, ['age': 22, 'name': 'alice', 'city': set('boston')], single)

Il existe moins d'options pour définir la cardinalité dans les versions mergeV() antérieures à 3.7.0. L'approche générale consiste à revenir à l'property()étape suivante :

g.mergeV([(T.id): '1234']). option(onMatch, sideEffect(property(single,'age', 20). property(set,'city','miami')).constant([:]))
Note

Cette approche ne fonctionnera que mergeV() si elle est utilisée avec une étape de démarrage. Vous ne pourriez donc pas mergeV() enchaîner une seule traversée, car la première étape utilisant cette syntaxe mergeV() après le début produira une erreur si le traverseur entrant est un élément graphique. Dans ce cas, vous souhaiterez diviser vos mergeV() appels en plusieurs demandes, chacune pouvant constituer une étape de départ.

ConfidentialitéConditions d'utilisation du sitePréférences de cookies
© 2025, Amazon Web Services, Inc. ou ses affiliés. Tous droits réservés.