Bonnes pratiques relatives au langage de manipulation de données

Cette page décrit les bonnes pratiques d'utilisation du langage de manipulation de données (LMD) et du LMD partitionné pour les bases de données de dialecte GoogleSQL et PostgreSQL.

Utiliser une clause WHERE pour réduire le nombre de lignes verrouillées

Les instructions LMD sont exécutées dans les transactions de lecture-écriture. Lorsque Spanner lit des données, il acquiert des verrous en lecture partagés sur les parties limitées des plages de lignes que vous consultez. Plus précisément, il n'acquiert ces verrous que sur les colonnes auxquelles vous accédez. Les verrous peuvent inclure des données qui ne répondent pas à la condition de filtre de la clause WHERE.

Lorsque Spanner modifie des données à l'aide d'instructions LMD, il acquiert des verrous exclusifs sur les données spécifiques que vous modifiez. De plus, il acquiert des verrous partagés de la même manière que lorsque vous consultez des données. Si votre requête inclut de grandes plages de lignes ou une table entière, les verrous partagés peuvent empêcher les autres transactions de progresser en parallèle.

Pour modifier les données aussi efficacement que possible, utilisez une clause WHERE permettant à Spanner de ne lire que les lignes nécessaires. Pour ce faire, appliquez un filtre sur la clé primaire ou sur la clé d'un index secondaire. La clause WHERE limite le champ d'application des verrous partagés et permet à Spanner de traiter la modification plus efficacement.

Par exemple, imaginons que l'un des musiciens de la table Singers change de prénom et que vous deviez mettre à jour ce prénom dans votre base de données. Vous pouvez exécuter l'instruction LMD suivante, mais elle oblige Spanner à analyser l'ensemble de la table et à acquérir des verrous partagés qui la couvrent dans sa totalité. Par conséquent, Spanner doit lire davantage de données que nécessaire, et les transactions simultanées ne peuvent pas modifier les données en parallèle :

-- ANTI-PATTERN: SENDING AN UPDATE WITHOUT THE PRIMARY KEY COLUMN
-- IN THE WHERE CLAUSE

UPDATE Singers SET FirstName = "Marcel"
WHERE FirstName = "Marc" AND LastName = "Richards";

Pour optimiser l'opération de mise à jour, incluez la colonne SingerId dans la clause WHERE. La colonne SingerId est la seule colonne de clé primaire pour la table Singers :

-- ANTI-PATTERN: SENDING AN UPDATE THAT MUST SCAN THE ENTIRE TABLE

UPDATE Singers SET FirstName = "Marcel"
WHERE FirstName = "Marc" AND LastName = "Richards"

S'il n'y a pas d'index sur FirstName ou LastName, vous devez analyser l'intégralité de la table pour trouver les chanteurs cibles. Si vous ne souhaitez pas ajouter d'index secondaire pour optimiser la mise à jour, incluez la colonne SingerId dans la clause WHERE.

La colonne SingerId est la seule colonne de clé primaire pour la table Singers. Pour le trouver, exécutez SELECT dans une transaction en lecture seule distincte avant la transaction de mise à jour :


  SELECT SingerId
  FROM Singers
  WHERE FirstName = "Marc" AND LastName = "Richards"

  -- Recommended: Including a seekable filter in the where clause

  UPDATE Singers SET FirstName = "Marcel"
  WHERE SingerId = 1;

Éviter d'utiliser des instructions LMD et des mutations dans la même transaction

Spanner met en tampon les insertions, les modifications et les suppressions effectuées à l'aide d'instructions LMD côté serveur. Les résultats sont visibles pour les instructions SQL et LMD ultérieures dans la même transaction. Ce processus est différent de celui de l'API Mutation, où Spanner met en tampon les mutations côté client et les envoie côté 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.

Évitez d'utiliser à la fois des instructions LMD et des mutations dans la même transaction. Si vous utilisez les deux dans la même transaction, vous devez tenir compte de l'ordre d'exécution dans le code de votre bibliothèque cliente. Si une transaction contient à la fois des instructions LMD et des mutations dans la même requête, Spanner exécute les instructions LMD avant les mutations.

Pour les opérations qui ne sont prises en charge qu'à l'aide de mutations, vous pouvez combiner des instructions LMD et des mutations dans la même transaction (par exemple, insert_or_update).

Si vous utilisez les deux, mettez en mémoire tampon les écritures seulement à la toute fin de la transaction.

Utiliser la fonction PENDING_COMMIT_TIMESTAMP pour écrire des horodatages de commit

GoogleSQL

Pour écrire l'horodatage de commit dans une instruction LMD, utilisez la fonction PENDING_COMMIT_TIMESTAMP. Spanner sélectionne l'horodatage de commit lorsque la transaction est enregistrée.

PostgreSQL

Pour écrire l'horodatage de commit dans une instruction LMD, utilisez la fonction SPANNER.PENDING_COMMIT_TIMESTAMP(). Spanner sélectionne l'horodatage de commit lorsque la transaction est enregistrée.

LMD partitionné, et fonctions de date et d'horodatage

Le LMD partitionné utilise une ou plusieurs transactions pouvant être exécutées et enregistrées à des moments différents. Si vous utilisez les fonctions de date ou d'horodatage, les lignes modifiées peuvent contenir des valeurs différentes.

Améliorer la latence avec le LMD par lots

Pour réduire la latence, utilisez le traitement LMD par lots pour envoyer plusieurs instructions LMD à Spanner lors d'un même aller-retour client-serveur.

Le LMD par lot peut appliquer des optimisations à des groupes d'instructions dans un lot pour permettre des mises à jour de données plus rapides et plus efficaces.

  • Exécuter des écritures avec une seule requête

    Spanner optimise automatiquement les groupes contigus d'instructions par lot INSERT, UPDATE ou DELETE similaires qui ont des valeurs de paramètre différentes, s'ils ne violent pas les dépendances de données.

    Prenons l'exemple d'un scénario dans lequel vous souhaitez insérer un grand nombre de nouvelles lignes dans une table appelée Albums. Pour permettre à Spanner d'optimiser toutes les instructions INSERT requises en une seule action côté serveur efficace, commencez par écrire une instruction LMD appropriée qui utilise des paramètres de requête SQL :

    INSERT INTO Albums (SingerId, AlbumId, AlbumTitle) VALUES (@Singer, @Album, @Title);
    

    Ensuite, envoyez à Spanner un lot LMD qui appelle cette instruction de manière répétée et continue, les répétitions ne différant que par les valeurs que vous liez aux trois paramètres de requête de l'instruction. Spanner optimise ces instructions LMD structurellement identiques en une seule opération côté serveur avant de l'exécuter.

  • Exécuter les écritures en parallèle

    Spanner optimise automatiquement les groupes contigus d'instructions LMD en les exécutant en parallèle lorsque cela ne viole pas les dépendances de données. Cette optimisation améliore les performances d'un plus grand nombre d'instructions LMD par lot, car elle peut s'appliquer à différents types d'instruction LMD (INSERT, UPDATE et DELETE), ainsi qu'aux instructions LMD paramétrées ou non.

    Par exemple, notre exemple de schéma comporte les tables Singers, Albums et Accounts. Albums est imbriqué dans Singers et stocke des informations sur les albums pour Singers. Le groupe d'instructions contigu suivant écrit de nouvelles lignes dans plusieurs tables et ne présente pas de dépendances de données complexes.

    INSERT INTO Singers (SingerId, Name) VALUES(1, "John Doe");
    INSERT INTO Singers (SingerId, Name) VALUES(2, "Marcel Richards");
    INSERT INTO Albums(SingerId, AlbumId, AlbumTitle) VALUES (1, 10001, "Album 1");
    INSERT INTO Albums(SingerId, AlbumId, AlbumTitle) VALUES (1, 10002, "Album 2");
    INSERT INTO Albums(SingerId, AlbumId, AlbumTitle) VALUES (2, 10001, "Album 1");
    UPDATE Accounts SET Balance = 100 WHERE AccountId = @AccountId;
    

    Spanner optimise ce groupe d'instructions LMD en les exécutant en parallèle. Les écritures sont appliquées dans l'ordre des instructions du lot et conservent la sémantique LMD du lot si une instruction échoue lors de l'exécution.

Activer le traitement par lot côté client dans JDBC

Pour les applications Java utilisant un pilote JDBC compatible avec Spanner, vous pouvez réduire la latence en activant le traitement par lot LMD côté client. Le pilote JDBC possède une propriété de connexion appelée auto_batch_dml qui, lorsqu'elle est activée, met en mémoire tampon les instructions LMD sur le client et les envoie à Spanner sous forme de lot unique. Cela peut réduire le nombre d'allers-retours vers le serveur et améliorer les performances globales.

Par défaut, auto_batch_dml est défini sur false. Pour l'activer, définissez-le sur true dans votre chaîne de connexion JDBC.

Exemple :

String url = "jdbc:cloudspanner:/projects/my-project/instances/my-instance/databases/my-database;auto_batch_dml=true";
try (Connection connection = DriverManager.getConnection(url)) {
    // Include your DML statements for batching here
}

Lorsque cette propriété de connexion est activée, Spanner envoie les instructions LMD mises en mémoire tampon sous forme de lot lorsqu'une instruction non LMD est exécutée ou lorsque la transaction en cours est validée. Cette propriété ne s'applique qu'aux transactions en lecture-écriture. Les instructions LMD en mode autocommit sont exécutées directement.

Par défaut, le nombre de mises à jour pour les instructions LMD mises en mémoire tampon est défini sur 1. Vous pouvez modifier ce paramètre en définissant la variable de connexion auto_batch_dml_update_count sur une autre valeur. Pour en savoir plus, consultez Propriétés de connexion compatibles avec JDBC.