Luchando contra los interbloqueos en Sql Server (II): Read Commited y el modo snapshot

Hace ya un tiempo escribía sobre cómo evitar los interbloqueos en Sql Server y como diagnosticar que les está causando cuando se producen. Hoy quiero contar un pequeño ‘truco’ que nos puede ayudar a, sin cambio alguno en el código de nuestra aplicación, reducir las probabilidades de sufrir un bloqueo. Este pequeño truco es un ‘antídoto’ que nos permite solucionar a menudo problemas de interbloqueos con un mínimo impacto sobre la aplicación. Además, esta técnica también puede mejorar el rendimiento de aquellas aplicaciones que estén sufriendo problemas de esperas en bloqueos entre lectores (selects) y escritores (updates e inserts).

El nivel de aislamiento por defecto de Sql Server es Read Commited. Esto quiere decir que este nivel es el más utilizado y que si no tienes ni idea de qué es el nivel de aislamiento este es el que estás utilizando. En este nivel de aislamiento, con el fin de evitar las lecturas sucias, cuando una consulta esta actualizando una fila, establece un bloqueo sobre la misma. Este bloqueo hace que quien quiere leer esta fila, se encuentre con que debe esperar. Este bloqueo en principio inocuo, puede se origen de interbloqueos en aplicaciones altamente concurrentes que no se hayan diseñado adecuadamente. Cuantos mas bloqueos, menos rendimiento, y más posibilidades de interbloqueos Tengo que decir, antes de que mi compañero Pablo Alvarez Doval, gurú de estos temas, me saque los colores, que he simplificado mucho la película, para no liar el asunto.

A partir de Sql Server 2005 se introdujo un nuevo modo de funcionamiento para el nivel de aislamiento Read Commited, que sigue evitando las lecturas sucias y que también evita ese posible bloque dañino del que hablábamos. Este modo es el modo Snapshot del nivel de aislamiento Read Commited, en el que, simplificando de nuevo el asunto, en lugar de establecer un bloqueo que para las lecturas, hace una copia en la sombra (snapshot) de las filas que se están modificando y si alguien intenta leer no lo bloquea sino que devuelve el valor copiado en la sombra. Resumiendo, no hay lecturas sucias como exige el nivel Read Commited y además no hay bloqueo.

Una ventaja adicional de este modo, es que es así y solo así como Oracle se comporta, luego si tu aplicación soporta Oracle y Sql Server, con este modo consigues el mismo comportamiento en ambas bases de datos en lo que a las transacciones Read Commited se refiere.

Evidentemente nada es gratis en la vida, y a cambio de no tener bloqueos, vamos a tener más trasiego en la base de datos temporal (TempDb) donde se almacenan los Snapshot y esta va a crecer en tamaño. En la mayoría de las ocasiones este es un mal menor.

Corolario: Si tienes una aplicación que está sufriendo bloqueos o interbloqueos, donde hay tablas que se escriben y leen concurrentemente, activar el modo Snapshot puede ayudarte a solucionar el problema.

Os dejo un script que establece el modo Snapshot para el nivel de aislamiento Read Commited para un base de datos:

-- Script que cambia en comportamiento del nivel de aislamiento
-- READ_COMMITED para que use snapshots (READ_COMMITTED_SNAPSHOT ON)

-- ¡OJO!: Ejecutar este script establece la base de datos a modo
-- de usuario único y por tanto termina las conexiones actuales a la
-- base de datos

-- Establecer la base de datos sobre la que haremos el cambio

USE <nombreDeBaseDeDatos>

-- Comprobar que la versión es SQL Server 2005 o superior
IF ((SELECT @@microsoftversion / 0x01000000) >= 9)
  BEGIN
    DECLARE  @sql VARCHAR(8000)
    
    SELECT @sql = 
         'ALTER DATABASE ' + DB_NAME() + ' SET SINGLE_USER WITH ROLLBACK IMMEDIATE ;
         
ALTER DATABASE ' + DB_NAME() + ' SET READ_COMMITTED_SNAPSHOT ON;
          ALTER DATABASE '
 + DB_NAME() + ' SET MULTI_USER; ' 
    EXEC(@sql)
  END
ELSE
  PRINT 'La versión de su SQL Server no soporta READ_COMMITTED_SNAPSHOT'
GO

¡Suerte en la batalla contra los bloqueos y los interbloqueos!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *