Cette page décrit les transactions dans Spanner et présente les interfaces de transaction en lecture-écriture, en lecture seule et en LMD partitionné de Spanner.
Une transaction dans Spanner est un ensemble de lectures et d'écritures qui s'exécutent de manière atomique à un moment logique unique dans des colonnes, des lignes et des tables d'une base de données.
Elles servent à effectuer des transactions dans une base de données Spanner. Une session représente un canal de communication logique avec le service de base de données Spanner. Les sessions peuvent exécuter une ou plusieurs transactions à la fois. Pour en savoir plus, consultez Sessions.
Types de transactions
Spanner accepte les types de transactions suivants, chacun étant conçu pour des modèles d'interaction de données spécifiques :
Lecture/écriture : ces transactions utilisent un verrouillage pessimiste et, si nécessaire, un commit en deux phases. Elles peuvent échouer et nécessiter des tentatives. Bien qu'ils soient limités à une seule base de données, ils peuvent modifier les données de plusieurs tables de cette base de données.
Lecture seule : ces transactions garantissent la cohérence des données pour plusieurs opérations de lecture, mais n'autorisent pas les modifications de données. Elles s'exécutent à un horodatage déterminé par le système pour assurer la cohérence, ou à un horodatage passé configuré par l'utilisateur. Contrairement aux transactions en lecture-écriture, elles ne nécessitent pas d'opération de commit ni de verrouillage, bien qu'elles puissent s'interrompre pour attendre la fin des opérations d'écriture en cours.
LMD partitionné : ce type de transaction exécute les instructions LMD en tant qu'opérations LMD partitionné. Il est optimisé pour les mises à jour et les suppressions de données à grande échelle, comme le nettoyage des données ou l'insertion de données en masse. Pour de nombreuses écritures qui n'ont pas besoin d'une transaction atomique, envisagez d'utiliser des écritures par lot. Pour en savoir plus, consultez Modifier des données à l'aide d'écritures par lot.
Transactions en lecture-écriture
Utilisez des transactions en lecture/écriture avec verrouillage pour lire, modifier et écrire des données de manière atomique n'importe où dans une base de données. Ce type de transaction est cohérent en externe.
Réduisez au minimum la durée d'activité d'une transaction. Des durées de transaction plus courtes augmentent la probabilité d'un commit réussi et réduisent les conflits.
Spanner tente de maintenir les verrous de lecture actifs tant que la transaction continue d'effectuer des lectures et qu'elle n'a pas été arrêtée par les opérations sessions.commit
ou sessions.rollback
.
Si le client reste inactif pendant de longues périodes, Spanner peut libérer les verrous de la transaction et l'annuler.
Conceptuellement, une transaction en lecture-écriture se compose de zéro ou plusieurs lectures ou instructions SQL suivies de sessions.commit
. À tout moment avant sessions.commit
, le client peut envoyer une requête sessions.rollback
pour annuler la transaction.
Pour effectuer une opération d'écriture qui dépend d'une ou de plusieurs opérations de lecture, utilisez une transaction en lecture-écriture avec verrouillage :
- Si vous devez valider une ou plusieurs opérations d'écriture de manière atomique, effectuez ces écritures dans la même transaction en lecture-écriture. Par exemple, si vous transférez 200 $ du compte A au compte B, effectuez les deux opérations d'écriture (diminuer le compte A de 200 $et augmenter le compte B de 200 $) et les lectures des soldes de compte initiaux dans la même transaction.
- Si vous souhaitez doubler le solde du compte A, effectuez les opérations de lecture et d'écriture dans la même transaction. Cela garantit que le système lit le solde avant de le doubler, puis de le mettre à jour.
- Si vous pouvez effectuer une ou plusieurs opérations d'écriture qui dépendent des résultats d'une ou de plusieurs opérations de lecture, effectuez ces écritures et ces lectures dans la même transaction en lecture-écriture, même si les opérations d'écriture ne sont pas exécutées. Par exemple, si vous souhaitez transférer 200 $du compte A au compte B uniquement si le solde actuel de A est supérieur à 500 $, incluez la lecture du solde de A et les opérations d'écriture conditionnelles dans la même transaction, même si le transfert n'a pas lieu.
Pour effectuer des opérations de lecture, utilisez une méthode de lecture unique ou une transaction en lecture seule :
- Si vous n'effectuez que des opérations de lecture et que vous pouvez les exprimer à l'aide d'une méthode de lecture unique, utilisez cette méthode ou une transaction en lecture seule. Contrairement aux transactions en lecture-écriture, les lectures uniques n'acquièrent pas de verrous.
Interface
Les bibliothèques clientes Spanner fournissent une interface pour l'exécution d'un ensemble de tâches dans une transaction en lecture/écriture, avec la possibilité de nouvelles tentatives d'exécution en cas d'échec de la transaction. Une transaction Spanner peut nécessiter plusieurs tentatives avant d'être validée.
Plusieurs situations peuvent entraîner l'abandon des transactions. Par exemple, si deux transactions tentent de modifier des données simultanément, un blocage peut se produire. Dans ce cas, Spanner abandonne une transaction pour permettre à l'autre de se poursuivre. Plus rarement, des événements transitoires dans Spanner peuvent également entraîner l'annulation de transactions.
Comme les transactions sont atomiques, une transaction annulée n'a aucun effet sur la base de données. Réessayez la transaction au cours de la même session pour améliorer le taux de réussite. Chaque nouvelle tentative qui génère une erreur ABORTED
augmente la priorité de verrouillage de la transaction.
Lorsque vous utilisez une transaction dans une bibliothèque cliente Spanner, vous définissez le corps de la transaction en tant qu'objet fonction. Cette fonction encapsule les lectures et les écritures effectuées sur une ou plusieurs tables de base de données. La bibliothèque cliente Spanner exécute cette fonction à plusieurs reprises jusqu'à ce que la transaction soit validée ou qu'une erreur pour laquelle les nouvelles tentatives ne sont pas possibles se produise.
Exemple
Supposons que vous disposiez d'une colonne MarketingBudget
dans la table Albums
:
CREATE TABLE Albums ( SingerId INT64 NOT NULL, AlbumId INT64 NOT NULL, AlbumTitle STRING(MAX), MarketingBudget INT64 ) PRIMARY KEY (SingerId, AlbumId);
Votre service marketing vous demande de transférer 200 000 $du budget de Albums
(2, 2)
vers Albums (1, 1)
, mais uniquement si la somme est disponible dans le budget de cet album. Vous devez utiliser le verrouillage des transactions en lecture-écriture pour cette opération, car la transaction peut effectuer des écritures en fonction du résultat de la lecture.
L'exemple suivant montre comment exécuter une transaction en lecture-écriture :
C++
C#
Go
Java
Node.js
PHP
Python
Ruby
Sémantique
Cette section décrit la sémantique des transactions en lecture-écriture dans Spanner.
Propriétés
Une transaction en lecture-écriture dans Spanner exécute un ensemble de lectures et d'écritures de manière atomique. L'horodatage d'exécution des transactions en lecture-écriture correspond au temps écoulé. L'ordre de sérialisation correspond à l'ordre d'horodatage.
Les transactions en lecture-écriture fournissent les propriétés ACID des bases de données relationnelles. Les transactions en lecture-écriture Spanner offrent des propriétés plus solides que les transactions ACID classiques.
Ces propriétés aident les développeurs d'applications à se concentrer sur l'exactitude de chaque transaction, sans se soucier de la protection de son exécution par rapport à d'autres transactions pouvant être exécutées simultanément.
Isolation pour les transactions en lecture-écriture
Une fois une transaction contenant une série de lectures et d'écritures validée, le message suivant s'affiche :
- La transaction renvoie des valeurs qui reflètent un instantané cohérent à l'horodatage de validation de la transaction.
- Les lignes ou plages vides le restent au moment de l'envoi.
- La transaction valide toutes les écritures au code temporel de validation de la transaction.
- Aucune transaction ne peut voir les écritures tant que la transaction n'est pas validée.
Les pilotes clients Spanner incluent une logique de nouvelle tentative de transaction qui masque les erreurs temporaires en relançant la transaction et en validant les données observées par le client.
Il en résulte que toutes les lectures et écritures semblent s'être produites à un moment donné, à la fois du point de vue de la transaction elle-même, et d'autres lecteurs et auteurs de la base de données Spanner. Cela signifie que les lectures et les écritures se produisent au même horodatage. Pour obtenir un exemple, consultez Sérialisabilité et cohérence externe.
Isolation pour les transactions de lecture
Lorsqu'une transaction en lecture-écriture n'effectue que des opérations de lecture, elle offre des garanties de cohérence similaires à celles d'une transaction en lecture seule. Toutes les lectures de la transaction renvoient des données d'un horodatage cohérent, y compris la confirmation des lignes inexistantes.
Une différence se produit lorsqu'une transaction en lecture-écriture est validée sans exécuter d'opération d'écriture. Dans ce scénario, rien ne garantit que les données lues dans la transaction sont restées inchangées dans la base de données entre l'opération de lecture et la validation de la transaction.
Pour garantir la fraîcheur des données et valider qu'elles n'ont pas été modifiées depuis leur dernière récupération, une lecture ultérieure est requise. Cette relecture peut être effectuée dans une autre transaction de lecture-écriture ou avec une lecture forte.
Pour une efficacité optimale, si une transaction effectue exclusivement des lectures, utilisez une transaction en lecture seule au lieu d'une transaction en lecture-écriture.
Atomicité, cohérence, durabilité
En plus de l'isolation, Spanner fournit les autres garanties de propriétés ACID :
- Atomicité. Une transaction est considérée comme atomique si toutes ses opérations sont effectuées avec succès, ou aucune. Si une opération d'une transaction échoue, l'intégralité de la transaction est annulée et revient à son état d'origine, ce qui garantit l'intégrité des données.
- Cohérence. Une transaction doit préserver l'intégrité des règles et des contraintes de la base de données. Une fois une transaction terminée, la base de données doit être dans un état valide, conforme aux règles prédéfinies.
- Durabilité. Une fois une transaction validée, ses modifications sont stockées de manière permanente dans la base de données et persistent en cas de défaillance du système, de coupure de courant ou d'autres perturbations.
Sérialisabilité et cohérence externe
Spanner offre de solides garanties transactionnelles, y compris la sérialisabilité et la cohérence externe. Ces propriétés garantissent que les données restent cohérentes et que les opérations se déroulent dans un ordre prévisible, même dans un environnement distribué.
La sérialisabilité garantit que toutes les transactions semblent s'exécuter les unes après les autres dans un ordre séquentiel unique, même si elles sont traitées simultanément. Pour ce faire, Spanner attribue des codes temporels de commit aux transactions, qui reflètent l'ordre dans lequel elles ont été validées.
Spanner offre une garantie encore plus solide, appelée cohérence externe. Cela signifie que non seulement les transactions sont validées dans un ordre reflété par leur horodatage de commit, mais que ces horodatages sont également alignés sur l'heure réelle. Cela vous permet de comparer les codes temporels de commit au temps réel, ce qui vous donne une vue cohérente et ordonnée à l'échelle mondiale de vos données.
En substance, si une transaction Txn1
est validée avant une autre transaction Txn2
en temps réel, l'horodatage de validation de Txn1
est antérieur à celui de Txn2
.
Prenons l'exemple suivant :
Dans ce scénario, pendant la période t
:
- La transaction
Txn1
lit les donnéesA
, prépare une écriture dansA
, puis est validée. - La transaction
Txn2
commence après le début de la transactionTxn1
. Il lit les donnéesB
, puis les donnéesA
.
Même si Txn2
a commencé avant la fin de Txn1, Txn2
observe les modifications apportées par Txn1
à A
. En effet, Txn2
lit A
après que Txn1
a validé son écriture dans A
.
Bien que les temps d'exécution de Txn1
et Txn2
puissent se chevaucher, leurs codes temporels de validation, c1
et c2
respectivement, imposent un ordre de transaction linéaire. Cela signifie :
- Toutes les lectures et écritures dans
Txn1
semblent s'être produites à un moment précis,c1
. - Toutes les lectures et écritures dans
Txn2
semblent s'être produites à un moment précis,c2
. - Il est essentiel de noter que
c1
est antérieur àc2
pour les écritures validées, même si les écritures ont eu lieu sur des machines différentes. SiTxn2
n'effectue que des lectures,c1
est antérieur ou égal àc2
.
Cet ordre fort signifie que si une opération de lecture ultérieure observe les effets de Txn2
, elle observe également les effets de Txn1
. Cette propriété est définie sur "true" pour toutes les transactions validées.
Garanties de lecture et d'écriture en cas d'échec d'une transaction
Si un appel à exécuter une transaction échoue, les garanties en lecture et en écriture dépendent de l'erreur qui a entraîné l'échec de l'appel de commit sous-jacent.
Par exemple, une erreur telle que "Ligne non trouvée" ou "Ligne déjà existante" signifie que l'écriture des mutations mises en mémoire tampon a rencontré une erreur, par exemple une des lignes que le client tente de mettre à jour n'existe pas. Dans ce cas, les lectures sont garanties cohérentes, les écritures ne sont pas appliquées et la non-existence de la ligne est également garantie cohérente avec les lectures.
Garanties de lecture et d'écriture en cas d'échec d'une transaction
Lorsqu'une transaction Spanner échoue, les garanties que vous recevez pour les lectures et les écritures dépendent de l'erreur spécifique rencontrée lors de l'opération commit
.
Par exemple, un message d'erreur tel que "Ligne non trouvée" ou "Ligne déjà existante" indique un problème lors de l'écriture des mutations mises en mémoire tampon. Cela peut se produire si, par exemple, une ligne que le client tente de mettre à jour n'existe pas. Dans les cas suivants :
- Les lectures sont cohérentes : toutes les données lues pendant la transaction sont garanties cohérentes jusqu'au moment de l'erreur.
- Les écritures ne sont pas appliquées : les mutations que la transaction a tentées ne sont pas validées dans la base de données.
- Cohérence des lignes : l'inexistence (ou l'état existant) de la ligne qui a déclenché l'erreur est cohérente avec les lectures effectuées dans la transaction.
Vous pouvez annuler à tout moment les opérations de lecture asynchrones dans Spanner sans affecter les autres opérations en cours dans la même transaction. Cette flexibilité est utile si une opération de niveau supérieur est annulée ou si vous décidez d'abandonner une lecture en fonction des résultats initiaux.
Toutefois, il est important de comprendre que demander l'annulation d'une lecture ne garantit pas son arrêt immédiat. Après une demande d'annulation, l'opération de lecture peut toujours :
- Opération terminée : il est possible que la lecture se termine et renvoie des résultats avant que l'annulation ne prenne effet.
- Échec pour une autre raison : la lecture peut se terminer en raison d'une autre erreur, telle qu'une annulation.
- Renvoyer des résultats incomplets : la lecture peut renvoyer des résultats partiels, qui sont ensuite validés dans le cadre du processus de commit de la transaction.
Il est également important de noter la distinction avec les opérations de transaction commit
: l'annulation d'une opération commit
interrompt l'intégralité de la transaction, sauf si elle a déjà été validée ou a échoué pour une autre raison.
Performances
Cette section décrit les problèmes qui affectent les performances des transactions en lecture/écriture.
Contrôle de simultanéité par verrouillage
Spanner permet à plusieurs clients d'interagir simultanément avec la même base de données. Pour maintenir la cohérence des données entre ces transactions simultanées, Spanner dispose d'un mécanisme de verrouillage qui utilise à la fois des verrous partagés et exclusifs.
Lorsqu'une transaction effectue une opération de lecture, Spanner acquiert des verrous en lecture partagés sur les données concernées. Ces verrous partagés permettent à d'autres opérations de lecture simultanées d'accéder aux mêmes données. Cette simultanéité est maintenue jusqu'à ce que votre transaction se prépare à valider ses modifications.
Pendant la phase de validation, à mesure que les écritures sont appliquées, la transaction tente de passer à des verrous exclusifs. Pour ce faire, il effectue les opérations suivantes :
- Bloque toutes les nouvelles demandes de verrou en lecture partagé sur les données concernées.
- Attend que tous les verrous en lecture partagés existants sur ces données soient levés.
- Une fois tous les verrous en lecture partagés annulés, il place un verrou exclusif, lui accordant un accès exclusif aux données pendant la durée de l'écriture.
Notes au sujet des verrous :
- Granularité : Spanner applique les verrous au niveau des lignes et des colonnes. Cela signifie que si la transaction
T1
détient un verrou sur la colonneA
de la lignealbumid
, la transactionT2
peut toujours écrire simultanément dans la colonneB
de la même lignealbumid
sans conflit. - Écritures sans lectures : pour les écritures sans lectures, Spanner n'a pas besoin de verrou exclusif. Il utilise plutôt un verrou partagé pour l'auteur. En effet, l'ordre d'application des écritures sans lectures est déterminé par leurs codes temporels de validation, ce qui permet à plusieurs rédacteurs d'opérer simultanément sur le même élément sans conflit. Un verrou exclusif n'est nécessaire que si votre transaction lit d'abord les données qu'elle a l'intention d'écrire.
- Index secondaires pour les recherches de lignes : lorsque vous effectuez des recherches de lignes dans une transaction en lecture/écriture, l'utilisation d'index secondaires peut améliorer considérablement les performances. En utilisant des index secondaires pour limiter les lignes analysées à une plage plus petite, Spanner verrouille moins de lignes dans la table, ce qui permet de modifier simultanément un plus grand nombre de lignes en dehors de cette plage spécifique.
- Accès exclusif aux ressources externes : les verrous internes de Spanner sont conçus pour assurer la cohérence des données au sein de la base de données Spanner elle-même. Ne les utilisez pas pour garantir un accès exclusif aux ressources en dehors de Spanner. Spanner peut annuler des transactions pour diverses raisons, y compris des optimisations système internes telles que le déplacement de données entre les ressources de calcul. Si une transaction est relancée (de manière explicite par votre code d'application ou de manière implicite par des bibliothèques clientes comme le pilote JDBC Spanner), la seule garantie est le maintien des verrous pendant la tentative de commit réussie.
- Statistiques de verrouillage : pour diagnostiquer et examiner les conflits de verrouillage dans votre base de données, vous pouvez utiliser l'outil d'introspection Statistiques de verrouillage.
Détection des blocages
Spanner détecte le blocage potentiel de plusieurs transactions et impose l'abandon de toutes les transactions sauf une. Prenons l'exemple suivant : Txn1
maintient un verrou sur l'enregistrement A
et attend un verrou sur l'enregistrement B
, tandis que Txn2
maintient un verrou sur l'enregistrement B
et attend un verrou sur l'enregistrement A
. Pour résoudre ce problème, l'une des transactions doit être annulée, ce qui libère son verrou et permet à l'autre de se poursuivre.
Spanner utilise l'algorithme standard wound-wait pour la détection des blocages. En arrière-plan, Spanner conserve une trace de l'âge de chaque transaction ayant entraîné des conflits de verrouillage. Il permet aux transactions plus anciennes d'abandonner les transactions plus récentes. Une transaction plus ancienne est une transaction dont la lecture, la requête ou le commit les plus anciens ont eu lieu plus tôt.
En donnant la priorité aux transactions plus anciennes, Spanner garantit que chaque transaction finit par obtenir des verrous une fois que son ancienneté est devenue suffisante pour lui donner une priorité plus élevée. Par exemple, une transaction plus ancienne nécessitant un verrou partagé pour l'auteur peut annuler une transaction plus récente contenant un verrou partagé pour le lecteur.
Exécution distribuée
Spanner peut exécuter des transactions sur des données couvrant plusieurs serveurs, mais cette fonctionnalité a un coût en termes de performances par rapport aux transactions sur un seul serveur.
Quels types de transactions peuvent être distribués ? Spanner peut répartir la responsabilité des lignes de la base de données sur plusieurs serveurs. En règle générale, une ligne et les lignes correspondantes dans les tables entrelacées sont diffusées par le même serveur, comme le sont deux lignes d'une même table ayant des clés proches. Spanner peut effectuer des transactions sur plusieurs lignes sur différents serveurs. Cependant, en règle générale, les transactions qui affectent de nombreuses lignes colocalisées sont plus rapides et moins coûteuses que celles qui affectent de nombreuses lignes dispersées dans la base de données ou dans une grande table.
Les transactions les plus efficaces dans Spanner n'incluent que les lectures et les écritures qui doivent être appliquées de manière atomique. Les transactions sont plus rapides lorsque toutes les lectures et écritures accèdent aux données dans une même partie de l'espace clé.
Transactions en lecture seule
En plus du verrouillage des transactions en lecture-écriture, Spanner offre des transactions en lecture seule.
Utilisez une transaction en lecture seule lorsque vous devez exécuter plusieurs lectures au même horodatage. Si vous pouvez exprimer votre lecture à l'aide de l'une des méthodes de lecture unique de Spanner, utilisez plutôt cette méthode. Les performances liées à l'utilisation d'un appel en lecture unique devraient être comparables à celles d'une lecture unique effectuée dans une transaction en lecture seule.
Si vous lisez une grande quantité de données, envisagez d'utiliser des partitions pour lire les données en parallèle.
Parce que les transactions en lecture seule n'effectuent aucune écriture, elles ne peuvent ni être verrouillées, ni bloquer les autres transactions. Les transactions en lecture seule observent un préfixe cohérent de l'historique de commit des transactions. De la sorte, votre application obtient toujours des données cohérentes.
Interface
Spanner fournit une interface pour l'exécution de tâches dans le contexte d'une transaction en lecture seule, avec la possibilité de nouvelles tentatives d'exécution en cas d'échec de la transaction.
Exemple
L'exemple suivant montre comment utiliser une transaction en lecture seule afin d'obtenir des données cohérentes pour deux lectures au même horodatage :
C++
C#
Go
Java
Node.js
PHP
Python
Ruby
Sémantique
Cette section décrit la sémantique des transactions en lecture seule.
Transactions en lecture seule d'instantanés
Lorsqu'une transaction en lecture seule s'exécute dans Spanner, elle effectue toutes ses lectures à un seul instant logique. Cela signifie que la transaction en lecture seule et tous les autres lecteurs et auteurs simultanés voient un instantané cohérent de la base de données à ce moment précis.
Ces transactions en lecture seule avec instantané offrent une approche plus simple pour les lectures cohérentes par rapport aux transactions en lecture-écriture avec verrouillage. Voici pourquoi :
- Aucun verrou : les transactions en lecture seule n'acquièrent pas de verrous. Au lieu de cela, elles fonctionnent en sélectionnant un code temporel Spanner et en exécutant toutes les lectures par rapport à cette version historique des données. Comme elles n'utilisent pas de verrous, elles ne bloquent pas les transactions en lecture/écriture simultanées.
- Aucune annulation : ces transactions ne sont jamais annulées. Bien qu'elles puissent échouer si leur code temporel de lecture choisi est récupéré par le garbage collector, la règle de récupération de mémoire par défaut de Spanner est généralement suffisamment généreuse pour que la plupart des applications ne rencontrent pas ce problème.
- Aucun commit ni rollback : les transactions en lecture seule ne nécessitent pas d'appels à
sessions.commit
ni àsessions.rollback
, et sont même empêchées de le faire.
Pour exécuter une transaction d'instantané, le client définit une limite d'horodatage, qui indique à Spanner comment sélectionner un horodatage de lecture. Voici les types de limites d'horodatage :
- Lectures fortes : ces lectures garantissent que vous verrez les effets de toutes les transactions validées avant le début de la lecture. Toutes les lignes d'une même lecture sont cohérentes. Toutefois, les lectures fortes ne sont pas reproductibles, même si elles renvoient un code temporel. La lecture à ce même code temporel est reproductible. Deux transactions en lecture seule fortes consécutives peuvent produire des résultats différents en raison d'écritures simultanées. Les requêtes sur les flux de modifications doivent utiliser cette limite. Pour en savoir plus, consultez TransactionOptions.ReadOnly.strong.
- Obsolescence exacte : cette option exécute des lectures à un code temporel que vous spécifiez, soit sous la forme d'un code temporel absolu, soit sous la forme d'une durée d'obsolescence par rapport à l'heure actuelle. Il vous permet d'observer un préfixe cohérent de l'historique des transactions globales jusqu'à cet horodatage et bloque les transactions simultanées qui pourraient être validées avec un horodatage inférieur ou égal à l'horodatage de lecture. Bien qu'il soit légèrement plus rapide que les modes d'obsolescence limitée, il peut renvoyer des données plus anciennes. Pour en savoir plus, consultez TransactionOptions.ReadOnly.read_timestamp et TransactionOptions.ReadOnly.exact_staleness.
- Obsolescence limitée : Spanner sélectionne l'horodatage le plus récent dans une limite d'obsolescence définie par l'utilisateur, ce qui permet l'exécution sur l'instance répliquée disponible la plus proche sans blocage. Toutes les lignes renvoyées sont cohérentes. Comme les lectures fortes, l'obsolescence limitée n'est pas reproductible, car différentes lectures peuvent s'exécuter à des horodatages différents, même avec la même limite. Ces lectures fonctionnent en deux phases (négociation de l'horodatage, puis lecture). Elles sont généralement légèrement plus lentes que les lectures à obsolescence exacte, mais elles renvoient souvent des résultats plus récents et sont plus susceptibles d'être exécutées sur un réplica local. Ce mode n'est disponible que pour les transactions en lecture seule à usage unique, car la négociation de l'horodatage nécessite de savoir à l'avance quelles lignes seront lues. Pour en savoir plus, consultez TransactionOptions.ReadOnly.max_staleness et TransactionOptions.ReadOnly.min_read_timestamp.
Transactions à LMD partitionné
Vous pouvez utiliser le LMD partitionné pour exécuter des instructions UPDATE
et DELETE
à grande échelle sans vous heurter à des limites de transaction ni verrouiller une table entière. Pour ce faire, Spanner partitionne l'espace clé et exécute les instructions LMD sur chaque partition dans une transaction en lecture-écriture distincte.
Pour utiliser le LMD non partitionné, vous exécutez des instructions dans des transactions en lecture-écriture que vous créez explicitement dans votre code. Pour en savoir plus, consultez Utiliser LMD.
Interface
Spanner fournit l'interface TransactionOptions.partitionedDml pour exécuter une seule instruction LMD partitionné.
Exemples
L'exemple de code suivant met à jour la colonne MarketingBudget
de la table Albums
.
C++
Vous utilisez la fonction ExecutePartitionedDml()
pour exécuter une instruction LMD partitionnée.
C#
Utilisez la méthode ExecutePartitionedUpdateAsync()
pour exécuter une instruction LMD partitionné.
Go
Utilisez la méthode PartitionedUpdate()
pour exécuter une instruction LMD partitionné.
Java
Utilisez la méthode executePartitionedUpdate()
pour exécuter une instruction LMD partitionné.
Node.js
Utilisez la méthode runPartitionedUpdate()
pour exécuter une instruction LMD partitionné.
PHP
Utilisez la méthode executePartitionedUpdate()
pour exécuter une instruction LMD partitionné.
Python
Utilisez la méthode execute_partitioned_dml()
pour exécuter une instruction LMD partitionné.
Ruby
Utilisez la méthode execute_partitioned_update()
pour exécuter une instruction LMD partitionné.
L'exemple de code suivant supprime les lignes de la table Singers
, en fonction de la colonne SingerId
.
C++
C#
Go
Java
Node.js
PHP
Python
Ruby
Sémantique
Cette section décrit la sémantique du LMD partitionné.
Comprendre l'exécution du LMD partitionné
Vous ne pouvez exécuter qu'une seule instruction LMD partitionné à la fois, que vous utilisiez une méthode de bibliothèque cliente ou la Google Cloud CLI.
Les transactions partitionnées ne sont pas compatibles avec les commits ni les restaurations. Spanner exécute et applique immédiatement l'instruction LMD. Si vous annulez l'opération ou si elle échoue, Spanner annule toutes les partitions en cours d'exécution et ne démarre aucune des partitions restantes. Toutefois, Spanner ne restaure pas les partitions déjà exécutées.
Stratégie d'acquisition de verrous pour le LMD partitionné
Pour réduire les conflits de verrouillage, le LMD partitionné n'acquiert de verrous en lecture que sur les lignes correspondant à la clause WHERE
. Les transactions indépendantes plus petites utilisées pour chaque partition conservent également les verrous moins longtemps.
Limites de transaction par session
Chaque session dans Spanner ne peut avoir qu'une seule transaction active à la fois. Cela inclut les lectures et les requêtes autonomes, qui utilisent en interne une transaction et sont comptabilisées dans cette limite. Une fois une transaction terminée, la session peut être immédiatement réutilisée pour la transaction suivante. Il n'est pas nécessaire de créer une session pour chaque transaction.
Récupération de mémoire pour les anciens codes temporels de lecture et les anciennes versions
Spanner effectue une récupération de mémoire pour collecter les données supprimées ou écrasées et récupérer de l'espace de stockage. Par défaut, les données de plus d'une heure sont récupérées. Spanner ne peut pas effectuer de lectures à des horodatages antérieurs à la VERSION_RETENTION_PERIOD
configurée, qui est d'une heure par défaut, mais qui peut être configurée jusqu'à une semaine. Lorsque les lectures deviennent trop anciennes pendant l'exécution, elles échouent et renvoient l'erreur FAILED_PRECONDITION
.
Requêtes sur les flux de modifications
Un flux de modifications est un objet de schéma que vous pouvez configurer pour surveiller les modifications de données dans une base de données entière, dans des tables spécifiques ou dans un ensemble défini de colonnes d'une base de données.
Lorsque vous créez un flux de modifications, Spanner définit une fonction de valeur de table (TVF) SQL correspondante. Vous pouvez utiliser cette TVF pour interroger les enregistrements de modifications dans le flux de modifications associé avec la méthode sessions.executeStreamingSql
. Le nom de la TVF est généré à partir du nom du flux de modifications et commence toujours par READ_
.
Toutes les requêtes sur les TVF de flux de modifications doivent être exécutées à l'aide de l'API sessions.executeStreamingSql
dans une transaction en lecture seule à usage unique avec un timestamp_bound
en lecture seule fort. Le TVF de flux de modifications vous permet de spécifier start_timestamp
et end_timestamp
pour la période. Tous les enregistrements de modifications au cours de la période de conservation sont accessibles à l'aide de ce timestamp_bound
en lecture seule. Tous les autres TransactionOptions
ne sont pas valides pour les requêtes de flux de modifications.
De plus, si TransactionOptions.read_only.return_read_timestamp
est défini sur true
, le message Transaction
décrivant la transaction renvoie une valeur spéciale de 2^63 - 2
au lieu d'un code temporel de lecture valide. Vous devez ignorer cette valeur spéciale et ne pas l'utiliser pour les requêtes ultérieures.
Pour en savoir plus, consultez Workflow de requête des flux de modifications.
Transactions inactives
Une transaction est considérée comme inactive si elle n'a pas de lectures ni de requêtes SQL en attente, et qu'elle n'en a pas démarré au cours des 10 dernières secondes. Spanner peut abandonner les transactions inactives pour les empêcher de conserver des verrous indéfiniment. Si une transaction inactive est abandonnée, le commit échoue et renvoie une erreur ABORTED
.
L'exécution périodique d'une petite requête, telle que SELECT 1
, dans la transaction peut l'empêcher de devenir inactive.