Tengo una consulta de lectura que ejecuto dentro de una transacción para poder especificar el nivel de aislamiento. Una vez completada la consulta, ¿qué debo hacer?
- Confirmar la transacción
- Revertir la transacción
- No hacer nada (lo que hará que la transacción se revierta al final del bloque de uso)
¿Cuáles son las implicaciones de hacer cada uno?
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: La pregunta no es si se debe usar una transacción o si hay otras formas de establecer el nivel de transacción. La pregunta es si hace alguna diferencia que una transacción que no modifica nada se confirme o se deshaga. ¿Existe alguna diferencia de rendimiento? ¿Afecta a otras conexiones? ¿Alguna otra diferencia?
sql
database
transactions
Stefan Moser
fuente
fuente
Respuestas:
Te comprometes. Período. No hay otra alternativa sensata. Si inició una transacción, debe cerrarla. Confirmar libera cualquier bloqueo que haya tenido y es igualmente sensato con los niveles de aislamiento ReadUncommitted o Serializable. Confiar en la reversión implícita, aunque quizás sea técnicamente equivalente, es simplemente una forma deficiente.
Si eso no lo ha convencido, imagínese al siguiente tipo que inserta una declaración de actualización en medio de su código y tiene que rastrear la reversión implícita que ocurre y eliminar sus datos.
fuente
Si no ha cambiado nada, puede usar COMMIT o ROLLBACK. Cualquiera de los dos liberará los bloqueos de lectura que haya adquirido y, dado que no ha realizado ningún otro cambio, serán equivalentes.
fuente
Si comienza una transacción, la mejor práctica es siempre confirmarla. Si se lanza una excepción dentro de su bloque de uso (transacción), la transacción se revertirá automáticamente.
fuente
En mi humilde opinión, puede tener sentido envolver consultas de solo lectura en transacciones ya que (especialmente en Java) puede decirle a la transacción que sea de "solo lectura", lo que a su vez el controlador JDBC puede considerar optimizar la consulta (pero no tiene que hacerlo, por lo que nadie le impedirá emitir un
INSERT
no obstante). Por ejemplo, el controlador de Oracle evitará por completo los bloqueos de tablas en consultas en una transacción marcada como de solo lectura, lo que gana mucho rendimiento en aplicaciones con mucha lectura.fuente
Considere las transacciones anidadas .
La mayoría de los RDBMS no admiten transacciones anidadas o intentan emularlas de forma muy limitada.
Por ejemplo, en MS SQL Server, una reversión en una transacción interna (que no es una transacción real, ¡MS SQL Server solo cuenta los niveles de transacción!) Revertirá todo lo que sucedió en la transacción más externa (que es la transacción real).
Algunos envoltorios de bases de datos pueden considerar una reversión en una transacción interna como una señal de que ha ocurrido un error y revertir todo en la transacción más externa, independientemente de si la transacción más externa se comprometió o revertió.
Por lo tanto, COMMIT es la forma segura, cuando no puede descartar que su componente sea utilizado por algún módulo de software.
Tenga en cuenta que esta es una respuesta general a la pregunta. El ejemplo de código soluciona inteligentemente el problema con una transacción externa abriendo una nueva conexión de base de datos.
En cuanto al rendimiento: según el nivel de aislamiento, los SELECT pueden requerir un grado variable de LOCK y datos temporales (instantáneas). Esto se limpia cuando se cierra la transacción. No importa si esto se hace a través de COMMIT o ROLLBACK. Puede haber una diferencia insignificante en el tiempo de CPU empleado: un COMMIT es probablemente más rápido de analizar que un ROLLBACK (dos caracteres menos) y otras diferencias menores. Obviamente, esto solo es cierto para operaciones de solo lectura.
Totalmente no solicitado: otro programador que pudiera llegar a leer el código podría asumir que un ROLLBACK implica una condición de error.
fuente
Solo una nota al margen, pero también puede escribir ese código así:
Y si reestructura las cosas solo un poco, es posible que también pueda mover el bloque de uso para IDataReader hacia arriba.
fuente
Si coloca el SQL en un procedimiento almacenado y agrega esto encima de la consulta:
entonces no tiene que pasar por ningún obstáculo en el código C #. Establecer el nivel de aislamiento de la transacción en un procedimiento almacenado no hace que la configuración se aplique a todos los usos futuros de esa conexión (que es algo de lo que debe preocuparse con otras configuraciones, ya que las conexiones están agrupadas). Al final del procedimiento almacenado, simplemente vuelve a lo que sea que se inicializó la conexión.
fuente
ROLLBACK se utiliza principalmente en caso de error o circunstancias excepcionales, y COMMIT en caso de finalización satisfactoria.
Deberíamos cerrar transacciones con COMMIT (para el éxito) y ROLLBACK (para el error), incluso en el caso de transacciones de solo lectura donde no parece importar. De hecho, importa, por coherencia y garantía de futuro.
Una transacción de solo lectura puede "fallar" lógicamente de muchas formas, por ejemplo:
Si COMMIT y ROLLBACK se usan correctamente para una transacción de solo lectura, continuará funcionando como se esperaba si se agrega código de escritura DB en algún momento, por ejemplo, para almacenamiento en caché, auditoría o estadísticas.
El ROLLBACK implícito solo debe usarse para situaciones de "error fatal", cuando la aplicación falla o se cierra con un error irrecuperable, falla de red, falla de energía, etc.
fuente
Dado que READ no cambia de estado, no haría nada. Realizar una confirmación no hará nada, excepto desperdiciar un ciclo para enviar la solicitud a la base de datos. No ha realizado una operación que haya cambiado de estado. Lo mismo ocurre con la reversión.
Sin embargo, debe asegurarse de limpiar sus objetos y cerrar sus conexiones a la base de datos. No cerrar sus conexiones puede generar problemas si este código se llama repetidamente.
fuente
Si establece AutoCommit falso, entonces SÍ.
En un experimento con JDBC (controlador Postgresql), descubrí que si la consulta de selección se rompe (debido al tiempo de espera), no puede iniciar una nueva consulta de selección a menos que retroceda.
fuente
En su ejemplo de código, donde tiene
// Haz algo útil
¿Está ejecutando una instrucción SQL que cambia datos?
Si no es así, no existe una transacción de "lectura" ... Solo los cambios de un Insertar, Actualizar y Eliminar declaraciones (declaraciones que pueden cambiar datos) están en una transacción ... Lo que está hablando son los bloqueos que SQL El servidor coloca los datos que está leyendo, debido a OTRAS transacciones que afectan esos datos. El nivel de estos bloqueos depende del nivel de aislamiento de SQL Server.
Pero no puede confirmar, o devolver nada, si su declaración SQL no ha cambiado nada.
Si está cambiando datos, puede cambiar el nivel de aislamiento sin iniciar explícitamente una transacción ... Cada declaración SQL individual está implícitamente en una transacción. iniciar explícitamente una Transacción solo es necesario para garantizar que 2 o más extractos estén dentro de la misma transacción.
Si todo lo que quiere hacer es establecer el nivel de aislamiento de la transacción, simplemente establezca el CommandText de un comando en "Establecer el nivel de aislamiento de la transacción de lectura repetible" (o el nivel que desee), establezca CommandType en CommandType.Text y ejecute el comando. (puede usar Command.ExecuteNonQuery ())
NOTA: Si está haciendo MÚLTIPLES declaraciones de lectura y desea que todas "vean" el mismo estado de la base de datos que la primera, entonces debe establecer el Nivel de aislamiento en la parte superior Lectura repetible o Serializable ...
fuente
¿Necesita impedir que otros lean los mismos datos? ¿Por qué utilizar una transacción?
@Joel - Mi pregunta estaría mejor redactada como "¿Por qué utilizar una transacción en una consulta de lectura?"
@Stefan: si va a usar AdHoc SQL y no un proceso almacenado, simplemente agregue CON (NOLOCK) después de las tablas en la consulta. De esta manera no incurrirá en gastos generales (aunque mínimos) en la aplicación y la base de datos para una transacción.
EDITAR @ Comentario 3: Ya que tenía "sqlserver" en las etiquetas de preguntas, había asumido que MSSQLServer era el producto de destino. Ahora que se ha aclarado ese punto, he editado las etiquetas para eliminar la referencia específica del producto.
Todavía no estoy seguro de por qué desea realizar una transacción en una operación de lectura en primer lugar.
fuente