sqlsql-serverrendimientobases-de-datosconcurrencia
Un
SELECT dentro de una transaccion abierta puede bloquear operaciones de escritura de forma silenciosa. No genera errores: las queries simplemente se quedan esperando. Veamos por que pasa y como evitarlo.Por que pasa
SQL Server usa
READ COMMITTED por defecto. Bajo este nivel de aislamiento, cada SELECT adquiere shared locks sobre las filas que lee. Normalmente se liberan al terminar la lectura, pero si el SELECT esta dentro de una transaccion explicita, los locks se mantienen hasta el COMMIT o ROLLBACK. Mientras tanto, cualquier UPDATE o DELETE sobre esas filas queda bloqueado.sql
-- Sesion 1: lectura con transaccion abierta
BEGIN TRAN;
SELECT *
FROM BodyMeasurements
WHERE userId = 42
AND measurementDate = CONVERT(date, GETDATE());
-- La transaccion sigue abierta...sql
-- Sesion 2: escritura bloqueada
UPDATE BodyMeasurements
SET weight = 78.3
WHERE userId = 42
AND measurementDate = CONVERT(date, GETDATE());
-- Esta query se queda esperando...Como evitarlo
NOLOCK
El hint
NOLOCK le indica a SQL Server que lea sin adquirir shared locks, eliminando el bloqueo a cambio de permitir lecturas de datos no confirmados.sql
SELECT *
FROM BodyMeasurements WITH (NOLOCK)
WHERE userId = 42
AND measurementDate = CONVERT(date, GETDATE());Transacciones cortas
La causa mas comun de estos bloqueos es una transaccion que permanece abierta mas tiempo del necesario. Abre la transaccion, ejecuta las operaciones de base de datos y cierrala de inmediato, sin incluir llamadas a APIs, validaciones externas ni envio de notificaciones.
Indices adecuados
Sin un indice adecuado, SQL Server recorre toda la tabla y adquiere locks sobre filas que ni siquiera necesita; un indice sobre las columnas del filtro limita los locks a las filas relevantes.
sql
CREATE INDEX IX_BodyMeasurements_User_Date
ON BodyMeasurements (userId, measurementDate);SNAPSHOT isolation
Con SNAPSHOT isolation, SQL Server usa versiones anteriores de las filas en lugar de locks, permitiendo lecturas consistentes sin bloquear escrituras.
sql
ALTER DATABASE MiBaseDeDatos
SET ALLOW_SNAPSHOT_ISOLATION ON;
SET TRANSACTION ISOLATION LEVEL SNAPSHOT;
BEGIN TRAN;
SELECT *
FROM BodyMeasurements
WHERE userId = 42
AND measurementDate = CONVERT(date, GETDATE());
COMMIT;Para terminar
Un
SELECT dentro de una transaccion abierta puede bloquear escrituras: conocer las estrategias para evitarlo te ahorra horas de depuracion.