Tenho uma consulta de leitura que executo dentro de uma transação para que possa especificar o nível de isolamento. Assim que a consulta for concluída, o que devo fazer?
- Confirme a transação
- Reverter a transação
- Não faça nada (o que fará com que a transação seja revertida no final do bloco de uso)
Quais são as implicações de fazer cada um?
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?
}
}
EDITAR: A questão não é se uma transação deve ser usada ou se existem outras maneiras de definir o nível da transação. A questão é se faz alguma diferença que uma transação que não modifica nada seja confirmada ou revertida. Existe uma diferença de desempenho? Isso afeta outras conexões? Alguma outra diferença?
sql
database
transactions
Stefan Moser
fonte
fonte
Respostas:
Você se compromete. Período. Não há outra alternativa sensata. Se você iniciou uma transação, deve fechá-la. A confirmação libera qualquer bloqueio que você possa ter e é igualmente sensata com os níveis de isolamento ReadUncommitted ou Serializable. Contar com a reversão implícita - embora talvez tecnicamente equivalente - é apenas uma má forma.
Se isso não o convenceu, imagine o próximo cara que insere uma instrução update no meio do seu código e tem que rastrear o rollback implícito que ocorre e remove seus dados.
fonte
Se você não mudou nada, então você pode usar um COMMIT ou um ROLLBACK. Qualquer um deles irá liberar todos os bloqueios de leitura que você adquiriu e, como você não fez nenhuma outra alteração, eles serão equivalentes.
fonte
Se você iniciar uma transação, a melhor prática é sempre confirmá-la. Se uma exceção for lançada dentro do seu bloco de uso (transação), a transação será automaticamente revertida.
fonte
IMHO pode fazer sentido agrupar consultas somente leitura em transações, pois (especialmente em Java) você pode dizer à transação para ser "somente leitura", que por sua vez o driver JDBC pode considerar a otimização da consulta (mas não precisa, então ninguém irá impedi-lo de emitir um
INSERT
no entanto). Por exemplo, o driver Oracle evitará completamente bloqueios de tabela em consultas em uma transação marcada como somente leitura, o que ganha muito desempenho em aplicativos altamente orientados para leitura.fonte
Considere as transações aninhadas .
A maioria dos RDBMSs não oferece suporte a transações aninhadas ou tenta emulá-las de uma forma muito limitada.
Por exemplo, no MS SQL Server, uma reversão em uma transação interna (que não é uma transação real, o MS SQL Server apenas conta os níveis de transação!) Irá reverter tudo o que aconteceu na transação externa (que é a transação real).
Alguns wrappers de banco de dados podem considerar um rollback em uma transação interna como um sinal de que ocorreu um erro e fazer rollback de tudo na transação externa, independentemente se a transação externa foi confirmada ou revertida.
Portanto, um COMMIT é o caminho seguro, quando você não pode descartar que seu componente seja usado por algum módulo de software.
Observe que esta é uma resposta geral à pergunta. O exemplo de código habilmente contorna o problema com uma transação externa abrindo uma nova conexão de banco de dados.
Com relação ao desempenho: dependendo do nível de isolamento, SELECTs podem exigir um grau variável de LOCKs e dados temporários (instantâneos). Isso é limpo quando a transação é fechada. Não importa se isso é feito via COMMIT ou ROLLBACK. Pode haver uma diferença insignificante no tempo gasto da CPU - um COMMIT é provavelmente mais rápido de analisar do que um ROLLBACK (dois caracteres a menos) e outras diferenças menores. Obviamente, isso só é verdade para operações somente leitura!
Totalmente não solicitado: outro programador que possa ler o código pode assumir que um ROLLBACK implica uma condição de erro.
fonte
Apenas uma observação lateral, mas você também pode escrever esse código assim:
E se você reestruturar as coisas apenas um pouco, poderá mover o bloco de uso do IDataReader para o topo também.
fonte
Se você colocar o SQL em um procedimento armazenado e adicioná-lo acima da consulta:
então você não tem que passar por nenhum obstáculo no código C #. Definir o nível de isolamento da transação em um procedimento armazenado não faz com que a configuração se aplique a todos os usos futuros dessa conexão (que é algo com que você deve se preocupar com outras configurações, uma vez que as conexões são agrupadas). No final do procedimento armazenado, ele volta para o que quer que a conexão tenha sido inicializada.
fonte
ROLLBACK é usado principalmente em caso de erro ou circunstâncias excepcionais, e COMMIT no caso de conclusão bem-sucedida.
Devemos fechar as transações com COMMIT (para sucesso) e ROLLBACK (para falha), mesmo no caso de transações somente leitura onde isso não parece importar. Na verdade, isso é importante, para consistência e segurança futura.
Uma transação somente leitura pode "falhar" logicamente de várias maneiras, por exemplo:
Se COMMIT e ROLLBACK forem usados corretamente para uma transação somente leitura, ele continuará a funcionar conforme o esperado se o código de gravação do DB for adicionado em algum ponto, por exemplo, para armazenamento em cache, auditoria ou estatísticas.
ROLLBACK implícito deve ser usado apenas para situações de "erro fatal", quando o aplicativo trava ou sai com um erro irrecuperável, falha de rede, falha de energia, etc.
fonte
Visto que um READ não muda de estado, eu não faria nada. Executar um commit não fará nada, exceto desperdiçar um ciclo para enviar a solicitação ao banco de dados. Você não executou uma operação que mudou de estado. Da mesma forma para a reversão.
No entanto, você deve limpar seus objetos e fechar suas conexões com o banco de dados. Não fechar suas conexões pode levar a problemas se este código for chamado repetidamente.
fonte
Se você definir AutoCommit como false, então YES.
Em um experimento com JDBC (driver Postgresql), descobri que, se a consulta de seleção for interrompida (devido ao tempo limite), você não poderá iniciar uma nova consulta de seleção a menos que faça rollback.
fonte
Em seu exemplo de código, onde você tem
// Faça algo útil
Você está executando uma instrução SQL que altera os dados?
Do contrário, não existe uma transação "Ler" ... Somente as alterações de uma instrução de inserção, atualização e exclusão (instruções que podem alterar dados) estão em uma transação ... O que você está falando são os bloqueios que o SQL O servidor coloca os dados que você está lendo, por causa de OUTRAS transações que afetam esses dados. O nível desses bloqueios depende do Nível de Isolamento do SQL Server.
Mas você não pode Commit, ou ROll Back nada, se sua instrução SQL não mudou nada.
Se você estiver alterando os dados, pode alterar o nível de isolamento sem iniciar explicitamente uma transação ... Cada instrução SQL individual está implicitamente em uma transação. iniciar explicitamente uma transação só é necessário para garantir que 2 ou mais instruções estejam dentro da mesma transação.
Se tudo o que você deseja fazer é definir o nível de isolamento da transação, basta definir o CommandText de um comando como "Definir leitura repetível do nível de isolamento da transação" (ou qualquer nível que desejar), definir CommandType como CommandType.Text e executar o comando. (você pode usar Command.ExecuteNonQuery ())
NOTA: Se você estiver fazendo instruções de leitura MÚLTIPLAS e quiser que todas elas "vejam" o mesmo estado do banco de dados como o primeiro, você precisará definir o nível de isolamento superior Leitura repetível ou serializável ...
fonte
Você precisa impedir que outras pessoas leiam os mesmos dados? Por que usar uma transação?
@Joel - Minha pergunta seria melhor formulada como "Por que usar uma transação em uma consulta de leitura?"
@Stefan - Se você for usar AdHoc SQL e não um procedimento armazenado, basta adicionar o WITH (NOLOCK) após as tabelas na consulta. Dessa forma, você não incorre em sobrecarga (embora mínima) no aplicativo e no banco de dados para uma transação.
EDIT @ Comentário 3: Como você tinha "sqlserver" nas tags de perguntas, presumi que MSSQLServer era o produto alvo. Agora que esse ponto foi esclarecido, editei as tags para remover a referência específica do produto.
Ainda não tenho certeza de por que você deseja fazer uma transação em uma operação de leitura em primeiro lugar.
fonte