J'ai une requête de lecture que j'exécute dans une transaction afin de pouvoir spécifier le niveau d'isolement. Une fois la requête terminée, que dois-je faire?
- Valider la transaction
- Annuler la transaction
- Ne rien faire (ce qui entraînera l'annulation de la transaction à la fin du bloc d'utilisation)
Quelles sont les implications de chacun?
using (IDbConnection connection = ConnectionFactory.CreateConnection())
{
using (IDbTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadUncommitted))
{
using (IDbCommand command = connection.CreateCommand())
{
command.Transaction = transaction;
command.CommandText = "SELECT * FROM SomeTable";
using (IDataReader reader = command.ExecuteReader())
{
// Read the results
}
}
// To commit, or not to commit?
}
}
MODIFIER: La question n'est pas de savoir si une transaction doit être utilisée ou s'il existe d'autres moyens de définir le niveau de transaction. La question est de savoir si cela fait une différence qu'une transaction qui ne modifie rien est validée ou annulée. Y a-t-il une différence de performance? Cela affecte-t-il d'autres connexions? Y a-t-il d'autres différences?
sql
database
transactions
Stefan Moser
la source
la source
Réponses:
Vous vous engagez. Période. Il n'y a pas d'autre alternative sensée. Si vous avez commencé une transaction, vous devez la clôturer. La validation libère tous les verrous que vous avez pu avoir et est tout aussi judicieuse avec les niveaux d'isolement ReadUncommitted ou Serializable. S'appuyer sur un retour en arrière implicite - bien que peut-être techniquement équivalent - n'est que médiocre.
Si cela ne vous a pas convaincu, imaginez le prochain type qui insère une instruction de mise à jour au milieu de votre code et doit retrouver la restauration implicite qui se produit et supprime ses données.
la source
Si vous n'avez rien changé, vous pouvez utiliser un COMMIT ou un ROLLBACK. L'un ou l'autre libère tous les verrous de lecture que vous avez acquis et puisque vous n'avez effectué aucune autre modification, ils seront équivalents.
la source
Si vous commencez une transaction, la meilleure pratique consiste toujours à la valider. Si une exception est levée dans votre bloc d'utilisation (transaction), la transaction sera automatiquement annulée.
la source
À mon humble avis, il peut être judicieux d'encapsuler les requêtes en lecture seule dans les transactions car (en particulier en Java), vous pouvez dire que la transaction est en «lecture seule», ce qui à son tour, le pilote JDBC peut envisager d'optimiser la requête (mais ce n'est pas obligatoire, donc personne vous empêchera
INSERT
néanmoins d'émettre un ). Par exemple, le pilote Oracle évitera complètement les verrous de table sur les requêtes dans une transaction marquée en lecture seule, ce qui gagne beaucoup de performances sur les applications fortement axées sur la lecture.la source
Considérez les transactions imbriquées .
La plupart des SGBDR ne prennent pas en charge les transactions imbriquées ou tentent de les émuler de manière très limitée.
Par exemple, dans MS SQL Server, une restauration dans une transaction interne (qui n'est pas une transaction réelle, MS SQL Server ne compte que les niveaux de transaction!) Annulera tout ce qui s'est passé dans la transaction la plus externe (qui est la transaction réelle).
Certains wrappers de base de données peuvent considérer une annulation dans une transaction interne comme un signe qu'une erreur s'est produite et annuler tout dans la transaction la plus externe, que la transaction la plus éloignée ait été validée ou annulée.
Ainsi, un COMMIT est le moyen le plus sûr, lorsque vous ne pouvez pas exclure que votre composant soit utilisé par un module logiciel.
Veuillez noter qu'il s'agit d'une réponse générale à la question. L'exemple de code résout intelligemment le problème avec une transaction externe en ouvrant une nouvelle connexion à la base de données.
Concernant les performances: en fonction du niveau d'isolement, les SELECT peuvent nécessiter un degré variable de LOCK et de données temporaires (instantanés). Ceci est nettoyé lorsque la transaction est fermée. Peu importe que cela se fasse via COMMIT ou ROLLBACK. Il peut y avoir une différence insignifiante dans le temps CPU passé - un COMMIT est probablement plus rapide à analyser qu'un ROLLBACK (deux caractères de moins) et d'autres différences mineures. Évidemment, cela n'est vrai que pour les opérations en lecture seule!
Pas du tout demandé: un autre programmeur qui pourrait lire le code pourrait supposer qu'un ROLLBACK implique une condition d'erreur.
la source
Juste une note latérale, mais vous pouvez également écrire ce code comme ceci:
Et si vous restructurez un peu les choses, vous pourrez peut-être aussi déplacer le bloc using pour l'IDataReader vers le haut.
la source
Si vous mettez le SQL dans une procédure stockée et ajoutez ceci au-dessus de la requête:
alors vous n'avez pas à sauter à travers les cercles dans le code C #. La définition du niveau d'isolation des transactions dans une procédure stockée ne provoque pas l'application du paramètre à toutes les utilisations futures de cette connexion (ce dont vous devez vous soucier avec d'autres paramètres puisque les connexions sont regroupées). À la fin de la procédure stockée, il revient simplement à celui avec lequel la connexion a été initialisée.
la source
ROLLBACK est principalement utilisé en cas d'erreur ou de circonstances exceptionnelles, et COMMIT en cas de réussite.
Nous devrions fermer les transactions avec COMMIT (pour le succès) et ROLLBACK (pour l'échec), même dans le cas de transactions en lecture seule où cela ne semble pas avoir d'importance. En fait, c'est important, pour la cohérence et la pérennité.
Une transaction en lecture seule peut logiquement «échouer» de plusieurs manières, par exemple:
Si COMMIT et ROLLBACK sont utilisés correctement pour une transaction en lecture seule, il continuera à fonctionner comme prévu si du code d'écriture DB est ajouté à un moment donné, par exemple pour la mise en cache, l'audit ou les statistiques.
Le ROLLBACK implicite ne doit être utilisé que pour les situations d '"erreur fatale", lorsque l'application plante ou se ferme avec une erreur irrécupérable, une panne de réseau, une panne de courant, etc.
la source
Étant donné qu'un READ ne change pas d'état, je ne ferais rien. Effectuer un commit ne fera rien, sauf gaspiller un cycle pour envoyer la requête à la base de données. Vous n'avez pas effectué d'opération dont l'état a changé. De même pour le rollback.
Vous devez cependant vous assurer de nettoyer vos objets et de fermer vos connexions à la base de données. Ne pas fermer vos connexions peut entraîner des problèmes si ce code est appelé à plusieurs reprises.
la source
Si vous définissez AutoCommit sur false, alors OUI.
Dans une expérience avec JDBC (pilote Postgresql), j'ai trouvé que si la requête de sélection est interrompue (en raison du délai d'expiration), vous ne pouvez pas lancer de nouvelle requête de sélection à moins de revenir en arrière.
la source
Dans votre exemple de code, où vous avez
// Faites quelque chose d'utile
Exécutez-vous une instruction SQL qui modifie les données?
Sinon, il n'y a pas de transaction "Read" ... Seules les modifications d'une instruction Insert, Update et Delete (instructions qui peuvent changer des données) sont dans une transaction ... Ce dont vous parlez, ce sont les verrous que SQL Le serveur met sur les données que vous lisez, en raison d'AUTRES transactions qui affectent ces données. Le niveau de ces verrous dépend du niveau d'isolation SQL Server.
Mais vous ne pouvez pas valider ou retourner quoi que ce soit si votre instruction SQL n'a rien changé.
Si vous modifiez des données, vous pouvez modifier le niveau d'isolement sans démarrer explicitement une transation ... Chaque instruction SQL individuelle est implicitement dans une transaction. démarrer explicitement une transaction n'est nécessaire que pour s'assurer que 2 ou plusieurs instructions se trouvent dans la même transaction.
Si tout ce que vous voulez faire est de définir le niveau d'isolement des transactions, définissez simplement CommandText d'une commande sur "Définir le niveau d'isolement des transactions en lecture répétable" (ou quel que soit le niveau souhaité), définissez le CommandType sur CommandType.Text et exécutez la commande. (vous pouvez utiliser Command.ExecuteNonQuery ())
REMARQUE: Si vous effectuez des instructions de lecture MULTIPLES et que vous souhaitez qu'elles "voient" toutes le même état de la base de données que la première, vous devez définir le niveau d'isolement supérieur Lecture répétable ou sérialisable ...
la source
Devez-vous empêcher les autres de lire les mêmes données? Pourquoi utiliser une transaction?
@Joel - Ma question serait mieux formulée comme "Pourquoi utiliser une transaction sur une requête de lecture?"
@Stefan - Si vous envisagez d'utiliser AdHoc SQL et non un processus stocké, ajoutez simplement le WITH (NOLOCK) après les tables de la requête. De cette façon, vous n'encourez pas la surcharge (quoique minime) dans l'application et la base de données pour une transaction.
EDIT @ Commentaire 3: Puisque vous aviez "sqlserver" dans les balises de question, j'avais supposé que MSSQLServer était le produit cible. Maintenant que ce point a été clarifié, j'ai modifié les balises pour supprimer la référence de produit spécifique.
Je ne sais toujours pas pourquoi vous souhaitez effectuer une transaction sur une opération de lecture en premier lieu.
la source