Comparer le LMD et les mutations

Le langage de manipulation de données (LMD) et les Mutations sont deux API dans Spanner qui vous permettent de modifier les données. Chacun offre des fonctionnalités de manipulation de données similaires. Cette page compare les deux approches.

Qu'est-ce que le langage de manipulation de données (LMD) ?

Le langage de manipulation de données (LMD) dans Spanner vous permet de manipuler les données dans les tables de votre base de données à l'aide des instructions INSERT, UPDATE et DELETE. Vous pouvez exécuter des instructions LMD à l'aide des bibliothèques clientes, de la Google Cloud console et de gcloud spanner.

Spanner propose les deux implémentations d'exécution LMD suivantes, chacune avec des propriétés différentes.

  • LMD standard : adapté aux charges de travail OLTP (Online Transaction Processing) standards.

    Pour en savoir plus et obtenir des exemples de code, consultez la section Utiliser le LMD.

  • LMD partitionné : conçu pour les mises à jour et les suppressions de manière groupée, comme dans les exemples suivants.

    • Nettoyage périodique et récupération de mémoire. Il s'agit par exemple de supprimer d'anciennes lignes ou de définir des colonnes sur NULL.

    • Remplissage de nouvelles colonnes avec des valeurs par défaut. Cette opération consiste, par exemple, à utiliser une instruction UPDATE pour définir la valeur d'une nouvelle colonne sur "False" lorsqu'elle est à NULL.

    Pour en savoir plus et obtenir des exemples de code, consultez la section Utiliser le LMD partitionné.

    Vous pouvez utiliser des écritures par lot pour un grand nombre d'opérations d'écriture sans opérations de lecture qui ne nécessitent pas de transactions atomiques. Pour en savoir plus, consultez Modifier des données à l'aide d'écritures par lot.

Que sont les mutations ?

Une mutation représente une séquence d'opérations (insertions, mises à jour, et suppressions) que Spanner applique de manière atomique à différentes lignes et tables d'une base de données. Vous pouvez inclure des opérations qui s'appliquent à différentes lignes ou à différentes tables d'une mutation. Après avoir défini une ou plusieurs mutations contenant une ou plusieurs écritures, vous devez appliquer la mutation pour valider les écritures. Chaque modification est appliquée dans l'ordre dans lequel elles ont été ajoutées à la mutation.

Pour en savoir plus et obtenir des exemples de code, consultez la section Insérer, mettre à jour et supprimer des données à l'aide de mutations.

Comparaison des fonctionnalités entre LMD et mutations

Le tableau suivant récapitule la prise en charge des opérations et des fonctionnalités courantes de la base de données par le LMD et les mutations.

Opérations LMD Mutations
Insérer des données Compatible Compatible
Supprimer des données Compatible Compatible
Mettre à jour des données Compatible Compatible
Insérer ou ignorer des données Compatible Non compatible
Lecture de vos écritures (RYW) Compatible Non compatible
Insérer ou mettre à jour des données (upsert) Compatible Compatible
Syntaxe SQL Compatible Non compatible
Vérification des contraintes Après chaque instruction Au moment du commit
Application de la limite de mutations (index compris) Par instruction (réinitialisation après l'exécution) Par commit (cumul)

La compatibilité du LMD et des mutations diffère pour les fonctionnalités suivantes.

  • Lecture de vos écritures : lecture des résultats non validés au sein d'une transaction active. Les modifications que vous effectuez à l'aide d'instructions LMD sont visibles pour les instructions suivantes dans la même transaction. Cette méthode est différente des mutations, avec lesquelles les modifications ne sont visibles dans les opérations de lecture (y-compris les lectures effectuées dans la même transaction) que lors du commit de la transaction. En effet, les mutations d'une transaction sont mises en mémoire tampon côté client (localement) et envoyées au serveur dans le cadre de l'opération de commit. En conséquence, les mutations dans la requête de commit ne sont pas visibles pour les instructions SQL ou LMD dans la même transaction.

  • Vérification des contraintes : Spanner vérifie les contraintes après chaque instruction LMD. Ce comportement est différent de celui applicable aux mutations : Spanner place ces mutations en tampon dans le client jusqu'au commit, puis vérifie les contraintes au moment du commit. L'évaluation des contraintes après chaque instruction LMD permet à Spanner de garantir que les données renvoyées par une requête ultérieure dans la même transaction sont cohérentes avec le schéma.

  • Application de la limite de mutations : pour le LMD, la limite de mutations de 80 000 (index compris) est appliquée par instruction et est réinitialisée après l'exécution de l'instruction. Cela permet à une seule transaction d'exécuter plusieurs instructions LMD qui dépassent collectivement 80 000 mutations, à condition qu'aucune instruction ne dépasse la limite. Pour l'API Mutation, qui ne peut être utilisée que dans le cadre de l'opération de commit, la limite de 80 000 mutations (index compris) est cumulative pour toutes les mutations du commit. Les deux méthodes sont soumises à la limite de taille de transaction de 100 Mio.

  • Syntaxe SQL : le LMD fournit une méthode traditionnelle pour manipuler les données. Vous pouvez réutiliser les compétences SQL pour modifier les données à l'aide de l'API LMD.

Bonne pratique : éviter de mélanger LMD et mutation dans la même transaction

Si une transaction contient à la fois des instructions LMD et des mutations dans la requête de commit, Spanner exécute les instructions LMD avant les mutations. Pour éviter de devoir prendre en compte l'ordre d'exécution dans le code de votre bibliothèque cliente, vous devez utiliser soit les instructions LMD, soit les mutations d'une même transaction, mais pas les deux.

L'exemple Java suivant illustre un comportement potentiellement surprenant. Le code insère deux lignes dans des Albums à l'aide de l'API Mutation. L'extrait appelle ensuite executeUpdate() pour mettre à jour les lignes nouvellement insérées et executeQuery() pour lire les albums mis à jour.

static void updateMarketingBudget(DatabaseClient dbClient) {
  dbClient
      .readWriteTransaction()
      .run(
          new TransactionCallable<Void>() {
            @Override
            public Void run(TransactionContext transaction) throws Exception {
               transaction.buffer(
                    Mutation.newInsertBuilder("Albums")
                        .set("SingerId")
                        .to(1)
                        .set("AlbumId")
                        .to(1)
                        .set("AlbumTitle")
                        .to("Total Junk")
                        .set("MarketingBudget")
                        .to(800)
                        .build());
               transaction.buffer(
                    Mutation.newInsertBuilder("Albums")
                        .set("SingerId")
                        .to(1)
                        .set("AlbumId")
                        .to(2)
                        .set("AlbumTitle")
                        .to("Go Go Go")
                        .set("MarketingBudget")
                        .to(200)
                        .build());

                // This UPDATE will not include the Albums inserted above.
                String sql =
                  "UPDATE Albums SET MarketingBudget = MarketingBudget * 2"
                      + " WHERE SingerId = 1";
                long rowCount = transaction.executeUpdate(Statement.of(sql));
                System.out.printf("%d records updated.\n", rowCount);

                // Read a newly updated record.
                sql =
                  "SELECT SingerId, AlbumId, AlbumTitle FROM Albums"
                      + " WHERE SingerId = 1 AND MarketingBudget < 1000";
                ResultSet resultSet =
                                 transaction.executeQuery(Statement.of(sql));
                while (resultSet.next()) {
                   System.out.printf(
                        "%s %s\n",
                        resultSet.getString("FirstName"),
                        resultSet.getString("LastName"));
                }
                return null;
              }
            });
}

En exécutant ce code, vous verriez 0 enregistrement mis à jour. Pourquoi ? Cela est dû aux modifications apportées à l'aide des Mutations, qui ne sont pas visibles pour les instructions suivantes jusqu'au commit de la transaction. Idéalement, nous aurions dû mettre en mémoire tampon les écritures seulement à la toute fin de la transaction.

Étape suivante