Puede un SELECT causar bloqueos en SQL Server? Si, y asi es como

Jorge SaavedraJorge Saavedra
·8 de enero, 2025·3 min de lectura
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.

Posts que podrian interesarte