Está en la página 1de 14

Durabilidad diferida de transacciones en

SQL Server 2014


por Rubén Garrigós | Ene 29, 2014 | SQL Server | 0 Comentarios
En ocasiones tenemos sistemas que soportan una carga de escritura elevada en hardware
con una entrada/salida inadecuada. Esto produce una percepción de rendimiento pobre
cuando las latencias de escritura en el log de transacciones son elevadas.
La nueva funcionalidad de durabilidad diferida en SQL Server 2014 reduce el impacto de la
latencia al mantener en memoria los registros del log de transacciones, empaquetándolos para
escribirlos en disco en batches, de forma que se reduzcan el número de operaciones
necesarias. La confirmación de las transacciones se realiza tan pronto se escriben los
registros en memoria, sin esperar a la confirmación del disco, por lo que solo deberíamos
aplicarlo cuando cierta pérdida de datos es aceptable. En cierta forma podemos verlo como
una mejora del buffer para transacciones in-flight donde la confirmación no se retrasa al flush
de dicho buffer a disco.
Afortunadamente la aplicación de la durabilidad diferida puede configurarse de forma granular.
A nivel de base de datos podemos deshabilitarla (valor por defecto), permitir su uso o forzar su
uso para todas las transacciones:
ALTER DATABASE BBDD SET DELAYED_DURABILITY = { DISABLED | ALLOWED | FORCED }
Para comprobar que ventaja de cara al rendimiento nos aporta esta funcionalidad vamos a
configurar tres bases de datos distintas cuya única diferencia será la ubicación física del
fichero de log:
La más lenta de ellas almacenará el log en un disco de 5400 rpm, la intermedia en un disco
SSD y la tercera en un disco SSD con caché DRAM. Para la prueba utilizaremos una tabla con
1 millón de registros sobre la que realizaremos updates aleatorios con varios threads. Para
crear la tabla utilizaremos el siguiente script:
create table test (id int identity (1,1) primary key, contador int)

insert into test (contador)


select top 1000000 0
from sys.objects s1, sys.objects s2, sys.objects s3, sys.objects s4, sys.objects s5
En la ejecución de este script ya podemos ver diferencias en base a la calidad del tipo de
almacenamiento. En el caso del disco de 5400 rpm la inserción ha necesitado 13 segundos,
en el caso del SSD 8 segundos y en el caso del SSD con caché únicamente 4 segundos.
Para generar los updates de forma aleatorios utilizaremos el siguiente script el cual
encapsularemos en un procedimiento almacenado:
set nocount on
declare @i int = 0
while (@i < 10000)
begin
update test
set contador = contador +1
where id=convert(int,rand()*1000000)
set @i=@i+1
end
A continuación lo que haremos es lanzar con 1, 10, 20 y 40 threads simultáneos la ejecución
de este procedimiento para las tres bases de datos. Repetiremos este proceso tres veces y
promediaremos el resultado. Una vez tengamos estos datos, activaremos la durabilidad
diferida y repetiremos el proceso. A continuación os muestro gráficamente los resultados
obtenidos, comenzando con la configuración de durabilidad completa:
Como podemos ver con durabilidad completa la utilización de medios más rápidos para el log
de transacciones nos aporta un aumento del número de updates por segundo bastante
considerable, especialmente en el momento que tenemos concurrencia de más de un thread.
Vemos también como el disco lento con 10 threads ya se satura y no se producen aumentos
significativos aumentando el número de threads.
El siguiente gráfico muestra los resultados con durabilidad diferida:
Lo primero que apreciamos es que ya con concurrencias muy bajas, con un 1 solo thread
ejecutando 10000 updates en serie, conseguimos valores de más de 20000 updates por
segundo, independientemente del disco utilizado. Vemos también que al aumentar a 10
threads concurrentes obtenemos un incremento de los updates por segundo muy significativo.
Con concurrencias mayores a 10 vemos que en el caso de discos rápidos, la curva se aplana
casi totalmente mientras que en el caso del disco lento sigue creciendo hasta los 40 threads.
Al final el throughput máximo que podemos obtener sigue ordenado en el orden lógico en
función del tipo de almacenamiento pero las diferencias existentes entre ellos son mucho
menores que con la durabilidad completa.
Observamos también que, con diferencia, el escenario más beneficiado por esta funcionalidad
es el del disco lento, que consigue acercarse mucho al rendimiento de los otros discos:
Como prueba adicional hemos repetido la creación de la tabla inicial con la inserción de
1000000 de registros. Podemos ver como el uso de durabilidad diferida prácticamente
equipara los tiempos a la inserción del escenario más rápido. En base a ello consideramos
que los escenarios de potencial uso se amplian a aquellos donde las operaciones de
modificación (inserción, modificación y borrado) tengan un peso importante.
La conclusión general que extraemos de esta funcionalidad es que la mejora en el rendimiento
nos ha sorprendido muy gratamente. Esperábamos aumentos de rendimiento pero no
esperábamos tanta diferencia en el caso de medios rápidos como el SSD + DRAM.
Las pruebas muestran que el impacto de esta funcionalidad es muy elevado en aquellas
cargas que generan muchas operaciones de entrada/salida en el log. El empaquetado de
estas operaciones y la confirmación anticipada de la transacción producen que las escrituras
sean de mayor tamaño con lo que el tiempo de respuesta del disco no es tan importante
comparado con el throughput. En casos de operaciones puntuales de un tamaño mayor
también el almacenamiento previo en RAM aporta ventajas de rendimiento.
En resumen, observamos que en sistemas donde el cuello de botella de la carga sean las
escrituras en el log y sea permisible una pequeña cantidad de pérdida de datos esta
funcionalidad marcará una gran diferencia de rendimiento a igualdad de hardware.

Descripción de los niveles de


aislamiento
 10/07/2018
 Tiempo de lectura: 3 minutos
 Colaboradores
o
o
o

Descargar controlador para JDBC

Las transacciones especifican un nivel de aislamiento que define el grado en que se


debe aislar una transacción de las modificaciones de recursos o datos realizadas
por otras transacciones. Los niveles de aislamiento se describen en función de los
efectos secundarios de la simultaneidad que se permiten, como las lecturas de
datos sucios o las lecturas fantasmas.

Los niveles de aislamiento de transacciones controlan lo siguiente:

 Controla si se realizan bloqueos cuando se leen los datos y qué tipos de


bloqueos se solicitan.
 Duración de los bloqueos de lectura.
 Si una operación de lectura que hace referencia a filas modificadas por otra
transacción:
o Se bloquea hasta que se libera el bloqueo exclusivo de la fila.
o Recupera la versión confirmada de la fila que existía en el momento en el
que se inició la instrucción o la transacción.
o Lee la modificación de los datos no confirmada.
La selección de un nivel de aislamiento de transacción no afecta a los bloqueos
adquiridos para proteger las modificaciones de datos. Siempre se obtiene un
bloqueo exclusivo en los datos modificados de una transacción, bloqueo que se
mantiene hasta que se completa la transacción, independientemente del nivel de
aislamiento seleccionado para la misma. En el caso de las operaciones de lectura,
los niveles de aislamiento de transacción definen básicamente el nivel de
protección contra los efectos de las modificaciones que realizan otras
transacciones.

Un nivel de aislamiento menor significa que los usuarios tienen un mayor acceso a
los datos simultáneamente, con lo que aumentan los efectos de la simultaneidad
que pueden experimentar, como las lecturas de datos sucios o la pérdida de
actualizaciones. Por el contrario, un nivel de aislamiento mayor reduce los tipos de
efectos de simultaneidad, pero requiere más recursos del sistema y aumenta las
posibilidades de que una transacción bloquee a otra. El nivel de aislamiento
apropiado depende del equilibrio entre los requisitos de integridad de los datos de
la aplicación y la sobrecarga de cada nivel de aislamiento. El nivel de aislamiento
superior, que es serializable, garantiza que una transacción recuperará exactamente
los mismos datos cada vez que repita una operación de lectura, aunque para ello
aplicará un nivel de bloqueo que puede afectar a los demás usuarios en los
sistemas multiusuario. El nivel de aislamiento menor, de lectura sin confirmar,
puede recuperar datos que otras transacciones han modificado pero no
confirmado. En este nivel se pueden producir todos los efectos secundarios de
simultaneidad, pero no hay bloqueos ni versiones de lectura, por lo que la
sobrecarga se reduce.
Notas

En la tabla siguiente se muestran los efectos secundarios de la simultaneidad que


permiten los distintos niveles de aislamiento.
Nivel de aislamiento Lectura de datos sucios lectura no repetible Fantasma

Lectura no confirmada Sí Sí Sí

Lectura confirmada no Sí Sí

Lectura repetible no no Sí
Nivel de aislamiento Lectura de datos sucios lectura no repetible Fantasma

Snapshot no no no

Serializable no no no

Las transacciones se deben ejecutar en un nivel de aislamiento de lectura repetible,


al menos, para evitar las pérdidas de actualizaciones que pueden producirse
cuando dos transacciones recuperan la misma fila, y a continuación la actualizan
según los valores recuperados originalmente. Si las dos transacciones actualizan las
filas con una única instrucción UPDATE y no basan la actualización en los valores
recuperados previamente, la pérdida de las actualizaciones no puede producirse en
el nivel de aislamiento predeterminado de lectura confirmada.

Para establecer el nivel de aislamiento para una transacción, puede utilizar el


método de setTransactionIsolation de la clase SQLServerConnection. Este método
acepta un valor int como argumento, que se basa en una de las constantes de
conexión, según se muestra a continuación:
JavaCopiar

con.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);

Para utilizar el nuevo nivel de aislamiento de instantánea de SQL Server, puede


utilizar una de las constantes SQLServerConnection:
JavaCopiar

con.setTransactionIsolation(SQLServerConnection.TRANSACTION_SNAPSHOT);

o puede utilizar:
JavaCopiar

con.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED + 4094);

Para obtener más información sobre los niveles de aislamiento de SQL Server, vea
"Niveles de aislamiento en Motor de base de datos" en los Libros en pantalla
de SQL Server.
Ejemplo#

Ejemplo de configuración del nivel de aislamiento:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;


SELECT * FROM Products WHERE ProductId=1;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ; --return to the default one
1. READ UNCOMMITTED : significa que una consulta en la transacción actual no
puede acceder a los datos modificados de otra transacción que aún no se ha
confirmado, ¡sin lecturas sucias! PERO, las lecturas no repetibles y las
lecturas fantasma son posibles, porque los datos todavía pueden ser
modificados por otras transacciones.
2. REPEATABLE READ : significa que una consulta en la transacción actual no
puede acceder a los datos modificados de otra transacción que aún no se ha
confirmado, ¡sin lecturas sucias! Ninguna otra transacción puede modificar
los datos que lee la transacción actual hasta que se complete, lo que elimina
las lecturas NO REPETIBLES. PERO, si otra transacción inserta NEW
ROWS y la consulta se ejecuta más de una vez, las filas fantasmas pueden
aparecer comenzando la segunda lectura (si coincide con la declaración
where de la consulta).
3. SNAPSHOT : solo puede devolver datos que existen al principio de la consulta.
Asegura la consistencia de los datos. Previene lecturas sucias, lecturas no
repetibles y lecturas fantasma. Para usar eso, se requiere configuración de
DB:

ALTER DATABASE DBTestName SET ALLOW_SNAPSHOT_ISOLATION ON;GO;


SET TRANSACTION ISOLATION LEVEL SNAPSHOT;

4. READ COMMITTED : aislamiento predeterminado del servidor SQL. Previene la


lectura de los datos que son modificados por otra transacción hasta que se
confirme. Utiliza el bloqueo compartido y el control de versiones de filas en
las tablas, lo que evita las lecturas sucias. Depende de la configuración de la
base de datos READ_COMMITTED_SNAPSHOT, si está habilitada, se
utiliza el control de versiones de fila. para habilitar - usa esto:

ALTER DATABASE DBTestName SET ALLOW_SNAPSHOT_ISOLATION ON;GO;


SET TRANSACTION ISOLATION LEVEL READ COMMITTED; --return to the default one

5. SERIALIZABLE : utiliza bloqueos físicos que se adquieren y mantienen hasta


el final de la transacción, lo que evita lecturas sucias, lecturas fantasma,
lecturas no repetibles. PERO, tiene un impacto en el rendimiento de la Base
de Datos, porque las transacciones concurrentes se serializan y se ejecutan
una por una.

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE ;


Leer no comprometido

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED


Este es el nivel de aislamiento más permisivo, ya que no causa ningún bloqueo.
Especifica que las declaraciones pueden leer todas las filas, incluidas las que se
han escrito en transacciones pero que aún no se han confirmado (es decir, aún
están en transacción). Este nivel de aislamiento
puede estar sujeto a "lecturas sucias".

Leer comprometido
SET TRANSACTION ISOLATION LEVEL READ COMMITTED

Este nivel de aislamiento es el segundo más permisivo. Previene lecturas sucias.


El comportamiento de READ COMMITTED depende de la configuración de
READ_COMMITTED_SNAPSHOT:

- Si se establece en DESACTIVADO (la configuración predeterminada), la


transacción utiliza bloqueos compartidos para evitar que otras
transacciones modifiquen las filas utilizadas por la transacción actual, así
como para bloquear la lectura de las filas modificadas por otras
transacciones.
- Si se establece en ACTIVADO, la READCOMMITTEDLOCK tabla
READCOMMITTEDLOCK se puede usar para solicitar un bloqueo compartido en
lugar del control de versiones de la fila para transacciones que se ejecutan
en el modo READ COMMITTED.
Nota: READ COMMITTED es el comportamiento predeterminado de SQL Server.

Lectura repetible
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ

Este nivel de aislamiento de la transacción es ligeramente menos permisivo que


READ COMMITTED, ya que los bloqueos compartidos se colocan en todos los datos
leídos por cada declaración en la transacción y se mantienen hasta que la
transacción se completa, en lugar de liberarse después de cada declaración.

Nota: use esta opción solo cuando sea necesario, ya que es más probable que
cause una degradación del rendimiento de la base de datos, así como puntos
muertos que READ COMMITTED.

Instantánea
SET TRANSACTION ISOLATION LEVEL SNAPSHOT

Especifica que los datos leídos por cualquier declaración en una transacción serán
la versión consistente de la transacción de los datos que existían al inicio de la
transacción, es decir, solo leerán los datos que se hayan confirmado antes de que
comience la transacción.
SNAPSHOT transacciones SNAPSHOT no solicitan ni causan ningún bloqueo en los
datos que se están leyendo, ya que solo está leyendo la versión (o instantánea) de
los datos que existían en el momento en que comenzó la transacción.

Una transacción que se ejecuta en el nivel de aislamiento SNAPSHOT lee solo sus
propios cambios de datos mientras se ejecuta. Por ejemplo, una transacción
podría actualizar algunas filas y luego leer las filas actualizadas, pero ese cambio
solo será visible para la transacción actual hasta que se confirme.

Nota: la opción de la base de datos ALLOW_SNAPSHOT_ISOLATION debe estar activada


antes de poder utilizar el nivel de aislamiento SNAPSHOT.

También podría gustarte