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?
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?
}
}
EDIT: 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 le fait qu'une transaction qui ne modifie rien soit validée ou annulée fait une différence. Y a-t-il une différence de performance? Cela affecte-t-il d'autres connexions? Y a-t-il d'autres différences?
Vous vous engagez. Période. Il n'y a pas d'autre alternative raisonnable. Si vous avez démarré une transaction, vous devez la fermer. Les validations validées libèrent tous les verrous que vous avez pu avoir et sont tout aussi pertinentes avec les niveaux d'isolation ReadUncommitted ou Serializable. S'appuyer sur un retour en arrière implicite - bien que peut-être techniquement équivalent - est simplement médiocre.
Si cela ne vous a pas convaincu, imaginez le type suivant qui insère une instruction de mise à jour au milieu de votre code et doit rechercher le retour en arrière implicite et supprime ses données.
Si vous n'avez rien changé, vous pouvez utiliser un COMMIT ou un ROLLBACK. Les deux verrouillent les verrous de lecture que vous avez acquis et, étant donné que vous n'avez apporté aucune autre modification, ils seront équivalents.
Si vous commencez une transaction, la meilleure pratique consiste toujours à la commettre. Si une exception est levée dans votre bloc d'utilisation (transaction), la transaction sera automatiquement annulée.
Considérons 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 annulation dans une transaction interne (qui n'est pas une transaction réelle, MS SQL Server ne comptabilise que les niveaux de transaction!) Annule tout ce qui s'est passé dans la transaction outmost transaction réelle).
Certaines enveloppes 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 à l'extérieur, que cette transaction ait été validée ou annulée.
Ainsi, un COMMIT est le moyen sûr, quand 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 à une base de données.
En ce qui concerne les performances: en fonction du niveau d’isolation, les SELECT peuvent nécessiter un degré de verrouillage et de données temporaires (instantanés) variable. Ceci est nettoyé lorsque la transaction est fermée. Peu importe que cela soit fait via COMMIT ou ROLLBACK. Il peut y avoir une différence insignifiante dans le temps passé par le processeur: 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!
Totalement non demandé: un autre programmeur susceptible de lire le code pourrait supposer qu'un ROLLBACK implique une condition d'erreur.
À mon humble avis, il peut être judicieux de placer les requêtes en lecture seule dans les transactions (en particulier en Java), vous pouvez indiquer que la transaction est en lecture seule, ce qui permet au pilote JDBC d'envisager d'optimiser la requête vous empêchera néanmoins d’émettre une INSERT
). 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 permet de gagner beaucoup en performances sur les applications fortement lues.
Juste une note, mais vous pouvez aussi écrire ce code comme ceci:
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())
{
// Do something useful
}
// To commit, or not to commit?
}
Et si vous restructurez un peu les choses, vous pourrez également déplacer le bloc using pour IDataReader vers le haut.
Si vous mettez le SQL dans une procédure stockée et ajoutez ceci au-dessus de la requête:
set transaction isolation level read uncommitted
alors vous n'avez pas à sauter dans les cerceaux du code C #. La définition du niveau d'isolation de transaction dans une procédure stockée n'entraîne pas l'application du paramètre à toutes les utilisations futures de cette connexion (vous devez vous soucier de cela avec d'autres paramètres, car les connexions sont mises en pool). À la fin de la procédure stockée, il revient simplement à la connexion initialisée.
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, cela a de l'importance 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 de fonctionner comme prévu si du code d'écriture de base de données est ajouté à un moment donné, par exemple. pour la mise en cache, l'audit ou les statistiques.
ROLLBACK implicite ne doit être utilisé que dans des situations "d'erreur fatale", lorsque l'application se bloque ou se ferme avec une erreur irrémédiable, une défaillance réseau, une panne d'alimentation, etc.
Étant donné qu'un READ ne change pas d'état, je ne ferais rien. Effectuer une validation ne fera rien, sauf un cycle pour envoyer la demande à la base de données. Vous n'avez pas effectué d'opération ayant changé d'état. De même pour la restauration.
Veillez toutefois à nettoyer vos objets et à 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.
Si vous définissez AutoCommit sur false, alors YES.
Dans une expérience avec JDBC (pilote Postgresql), j'ai constaté que si la requête sélectionnée était interrompue (en raison du délai d'attente), vous ne pouviez pas lancer de nouvelle requête de sélection à moins que vous ne l'annuliez.