Dormir SPID bloqueando otras transacciones

16

Realmente tengo problemas para rastrear algunos bloqueos que estamos experimentando.

El estado del SPID de bloqueo de raíz es 'durmiendo', el cmd es 'EN ESPERA DE MANDO' y el sqltextes SET TRANSACTION ISOLATION LEVEL READ COMMITTED.

Cuando veo el informe Top Transactions by Blocked Transactions Count, la instrucción SQL de bloqueo es '-'.

He realizado un seguimiento en el SQL y cuando ocurre el bloqueo, rastreando el SPID de bloqueo de raíz, pero realmente no me ha llevado a ninguna parte. La última declaración de rastreo es la misma que la sqltextanterior SET TRANSACTION ISOLATION LEVEL READ COMMITTED.

Verifiqué todos los procedimientos almacenados relacionados que puedo encontrar para asegurarme de que tengan sentencias TRY / CATCH BEGIN TRAN / COMMIT TRAN / ROLLBACK TRAN (utilizamos procedimientos almacenados para todo, por lo que no se ejecutan sentencias independientes). Este problema acaba de comenzar a suceder en las últimas 24 horas y nadie afirma haber realizado ningún cambio en el sistema.

Solución: uno de nuestros procedimientos almacenados que rara vez se usaba tenía un error con una inserción (el número de columnas no coincidía), pero aún estamos confundidos sobre lo que estaba sucediendo exactamente.

Al observar toda la información de rastreo, la instrucción EXEC para este procedimiento almacenado se enumeraba a veces, pero NUNCA justo antes de que ocurriera el BLOQUEO en el SPID de bloqueo. Parecía que cuando comenzaba a bloquear, la traza no registraba la ejecución de la misma (ni ninguna de las declaraciones dentro de ella). Sin embargo, hay otros momentos en que la traza registró su ejecución y no se produjo ningún bloqueo.

El informe de error del procedimiento almacenado provino de un usuario, y pude encontrar múltiples declaraciones EXEC en las trazas y ejecutarlas en SSMS. En ningún momento cuando los ejecuté tuvimos algún bloqueo o se bloquearon. Funcionaron como se esperaba (el bloque catch se disparó y retiró la transacción después del error). Después de resolver la solución del procedimiento almacenado, no hemos vuelto a ver el problema.

Puntilla
fuente
¿Supongo que el nombre de host del SPID de bloqueo no ayudó en absoluto?
Jon Seigel
No, es solo la IP de uno de nuestros servidores web ... Tenemos otra idea para cambiar cada inicio de sesión SQL para cada llamada SPROC durante el proceso de inicio de sesión / registro (que creemos es donde se produce el error) a un nombre de usuario separado que puede ayúdenos a aislar qué SPROC podría estar causando el bloqueo.
Brad
1
TRY / CATCH no detectará errores de compilación y una inserción de columna no coincidente sería un error de compilación. Esto tampoco desencadenaría muchos de los XX: Eventos de perfil completo.
Remus Rusanu
1
En realidad, no es un error de compilación en este caso porque el desarrollador genio usó INSERT INTO [tabla] SELECT * de [othertable] y no quedó atrapado en el mismo nivel. Ejecuté el SPROC en Desarrollo 1000 veces desde ColdFusion en 3 sesiones simultáneas y nunca dejó abierta una transacción como en Producción.
Brad

Respuestas:

10

Según los comentarios, supongo que tuvo un tiempo de espera de comando del lado del cliente que ha cancelado la consulta SQL. Esto no revierte la transacción porque la conexión permanece abierta en SQL Server debido a la agrupación de conexiones.

Por lo tanto, debe usar SET XACT_ABORT ON o agregar algún código de reversión de cliente

Vea SQL Server Transaction Timeout para todos los detalles sangrientos

gbn
fuente
Todos nuestros SPROC contienen bloques TRY / CATCH y declaraciones BEGIN TRAN / COMMIT TRAN / ROLLBACK TRAN, ROLLBACK está en CATCH. ¿XACT_ABORT todavía tendría un efecto?
Brad
@Brad: sí. Mira mi enlace. El bloqueo no se alcanza en CommandTimeout
gbn
gbn: Gracias. Todavía estoy confundido. Nuestras conexiones están configuradas para nunca agotar el tiempo de espera (0). Entonces, está diciendo que si reutilizamos las conexiones, y una conexión ejecuta un SPROC con un error (que tiene bloques TRY / CATCH y TRAN), de alguna manera nunca puede ejecutar el ROLLBACK en el bloque CATCH bloqueando así las tablas y manteniendo una transacción ¿abierto? Eso no tiene sentido para mí.
Brad
@Brad: un SPROC con un error golpeará el bloque CATCH. No dije lo contrario o diferente. Pero mi enlace indica lo que sucede si tiene un CommandTimeout, que es diferente a un ConnectionTimeout. El cliente dice "abortar" y SQL Server detiene el procesamiento. Ergo, el bloqueo o reversión de CATCH nunca se ve afectado
gbn
No creo que tengamos un CommandTimeout especificado. Todos nuestros procedimientos almacenados se prueban con sqlstress y deben realizar menos de 1000 ms en 10 usuarios 10 iteraciones (como mínimo). Todavía estoy muy confundido acerca de lo que sucedió, pero estoy actualizando la pregunta con lo que encontramos que era el problema.
Brad
9

Use most_recent_sql_handle en sys.dm_exec_connections para ver la última instrucción que se ejecutó.

SELECT  t.text,
        QUOTENAME(OBJECT_SCHEMA_NAME(t.objectid, t.dbid)) + '.'
        + QUOTENAME(OBJECT_NAME(t.objectid, t.dbid)) proc_name,
        c.connect_time,
        s.last_request_start_time,
        s.last_request_end_time,
        s.status
FROM    sys.dm_exec_connections c
JOIN    sys.dm_exec_sessions s
        ON c.session_id = s.session_id
CROSS APPLY sys.dm_exec_sql_text(c.most_recent_sql_handle) t
WHERE   c.session_id = 72;--your blocking spid

También verifique si hay transacciones abiertas para ese spid

SELECT  st.transaction_id,
        at.name,
        at.transaction_begin_time,
        at.transaction_state,
        at.transaction_status
FROM    sys.dm_tran_session_transactions st
JOIN    sys.dm_tran_active_transactions at
        ON st.transaction_id = at.transaction_id
WHERE   st.session_id = 72;--your blocking spid
Sebastian Meine
fuente
También puede usar DBCC INPUTBUFFER(spid)para ver el último SQL ejecutado.
Mike Fal
He usado todos esos y el último comando siempre es lo que puse en mi publicación original: ESTABLECER NIVEL DE AISLAMIENTO DE LA TRANSACCIÓN LEÍDO COMPROMETIDO. También ejecuté DBCC OPENTRAN y puedo ver que hay una transacción abierta para el PID de bloqueo.
Brad
Mi primera selección también le da el nombre del procedimiento, si la declaración es parte de un procedimiento.
Sebastian Meine
Le aseguro que no usamos consultas ad hoc de nuestros servidores web, y cuando ejecuto esa primera consulta, incluso sin la cláusula WHERE, solo obtengo el SPROC nombrado en un puñado de sesiones SQL, el resto de esa columna es NULL.
Brad
Me doy cuenta de que tengo toneladas de sesiones que dicen 'ESTABLECER EL NIVEL DE AISLAMIENTO DE LA TRANSACCIÓN LEÍDO COMPROMETIDO' y todas son de ColdFusion (la secuencia de comandos principal utilizada en nuestros servidores web). Quizás ColdFusion cuando está inactivo emite esa declaración para mantener abierta una conexión (ya que está configurada para mantener las conexiones abiertas).
Brad
4

¿Has intentado usar sp_whoisactive de Adam Machanic ? Hay una opción para obtener el comando externo para ver si realmente está dentro de un proceso. Podría ser que la aplicación mantenga abierta una transacción en lugar de confirmarla. Intente mirar DBCC OPENTRAN también.

Eric Humphrey - lotsahelp
fuente
Gracias por DBCC OPENTRAN. Sí me dice que el PID de bloqueo tiene una transacción abierta, pero no hay más detalles disponibles. sp_whoisactive devuelve la misma información sobre el proceso bloqueado que he podido obtener por mi cuenta. Todavía no hay detalles sobre lo que está sucediendo que no sea 'CONFIGURAR EL NIVEL DE AISLAMIENTO DE LA TRANSACCIÓN LEÍDO COMPROMETIDO'
Brad