sql >> Base de Datos >  >> RDS >> Access

TRANSACTION_MUTEX y acceso a transacciones multisesión

Recientemente encontré un alto TRANSACTION_MUTEX tiempo de espera acumulado en un sistema cliente. No podía recordar un caso en el que vi este tipo de espera cerca de la parte superior de la lista de "esperas altas" y tenía curiosidad acerca de qué factores podrían impulsar este tipo de tiempo de espera general.

La definición de Books Online de TRANSACTION_MUTEX es que "ocurre durante la sincronización del acceso a una transacción por múltiples lotes". No hay muchas áreas dentro del motor de SQL Server que expongan este tipo de funcionalidad, por lo que mi investigación se redujo a las siguientes tecnologías:

  • El obsoleto sp_getbindtoken y sp_bindsession procedimientos almacenados del sistema utilizados para manejar conexiones vinculadas
  • Transacciones distribuidas
  • MARS (conjuntos de resultados activos múltiples)

Mi objetivo era probar cada tecnología y ver si influía en TRANSACTION_MUTEX tipo de espera.

La primera prueba que realicé usó el obsoleto sp_getbindtoken y sp_bindsession procedimientos almacenados. El sp_getbindtoken devuelve un identificador de transacción que luego puede ser utilizado por sp_bindsession para vincular varias sesiones en la misma transacción.

Antes de cada escenario de prueba, me aseguré de borrar las estadísticas de espera de mi instancia de SQL Server de prueba:

DBCC SQLPERF('waitstats', CLEAR);
GO

Mi instancia de prueba de SQL Server estaba ejecutando SQL Server 2012 SP1 Developer Edition (11.0.3000). Utilicé la base de datos de ejemplo de crédito, aunque podría usar cualquier otro tipo de base de datos de ejemplo como AdventureWorks si quisiera, ya que el esquema y la distribución de datos no son directamente relevantes para el tema de este artículo y no eran necesarios para conducir el TRANSACTION_MUTEX tiempo de espera.

sp_getbindtoken/sp_bindsession

En la ventana de la primera sesión de SQL Server Management Studio, ejecuté el siguiente código para comenzar una transacción y generar el token de vinculación para el alistamiento de las otras sesiones planificadas:

USE Credit;
GO
 
BEGIN TRANSACTION;
 
DECLARE @out_token varchar(255);
 
EXECUTE sp_getbindtoken @out_token OUTPUT;
 
SELECT @out_token AS out_token;
GO

Esto devolvió un @out_token de S/Z5_GOHLaGY<^i]S9LXZ-5---.fE--- . En dos ventanas de consulta separadas de SQL Server Management Studio, ejecuté el siguiente código para unirme a las sesiones existentes (accediendo a la transacción compartida):

USE Credit;
GO
 
EXEC sp_bindsession 'S/Z5_GOHLaGY<^i]S9LXZ-5---.fE---';

Y con la ventana de la primera sesión aún abierta, inicié el siguiente bucle para actualizar la tabla de la tabla de cargos con una fecha de cargo igual a la fecha y hora actuales, y luego ejecuté la misma lógica en las otras dos ventanas (tres sesiones activas en la bucle):

WHILE 1 = 1 
BEGIN
    UPDATE  dbo.charge
    SET     charge_dt = SYSDATETIME();
END

Después de unos segundos, cancelé cada consulta en ejecución. De las tres sesiones, solo una pudo realizar actualizaciones, aunque las otras dos sesiones se unieron activamente a la misma transacción. Y si miro el TRANSACTION_MUTEX tipo de espera, puedo ver que efectivamente se incrementó:

SELECT  [wait_type],
        [waiting_tasks_count],
        [wait_time_ms],
        [max_wait_time_ms],
        [signal_wait_time_ms]
FROM sys.dm_os_wait_stats
WHERE wait_type = 'TRANSACTION_MUTEX';

Los resultados de esta prueba en particular fueron los siguientes:

wait_type            waiting_tasks_count   wait_time_ms   max_wait_time_ms   signal_wait_time_ms
TRANSACTION_MUTEX    2                     181732         93899              0

Así que veo que había dos tareas en espera (las dos sesiones que simultáneamente intentaban actualizar la misma tabla a través del bucle). Como no había ejecutado SET NOCOUNT ON , pude ver que solo el primero ejecutó UPDATE loop obtuvo cambios. Probé esta técnica similar usando algunas variaciones diferentes (por ejemplo, cuatro sesiones generales, con tres en espera) y el TRANSACTION_MUTEX incrementando mostró un comportamiento similar. También vi el TRANSACTION_MUTEX acumulación al actualizar simultáneamente una tabla diferente para cada sesión, por lo que no se requirieron modificaciones contra el mismo objeto en un bucle para reproducir el TRANSACTION_MUTEX acumulación de tiempo de espera.

Transacciones distribuidas

Mi próxima prueba consistió en ver si TRANSACTION_MUTEX el tiempo de espera se incrementó para las transacciones distribuidas. Para esta prueba, utilicé dos instancias de SQL Server y un servidor vinculado conectado entre los dos. MS DTC se estaba ejecutando y configurado correctamente, y ejecuté el siguiente código que realizó un DELETE local y un DELETE remoto a través del servidor vinculado y luego deshace los cambios:

USE Credit;
GO
 
SET XACT_ABORT ON;
 
-- Assumes MS DTC service is available, running, properly configured
BEGIN DISTRIBUTED TRANSACTION;
 
DELETE [dbo].[charge] WHERE charge_no = 1;
DELETE [JOSEPHSACK-PC\AUGUSTUS].[Credit].[dbo].[charge] WHERE charge_no = 1;
 
ROLLBACK TRANSACTION;

La TRANSACTION_MUTEX no mostró actividad en el servidor local:

wait_type            waiting_tasks_count   wait_time_ms   max_wait_time_ms   signal_wait_time_ms
TRANSACTION_MUTEX    0                     0              0                  0

Sin embargo, el recuento de tareas en espera se incrementó en el servidor remoto:

wait_type            waiting_tasks_count   wait_time_ms   max_wait_time_ms   signal_wait_time_ms
TRANSACTION_MUTEX    1                     0              0                  0

Así que mi expectativa de ver esto se confirmó, dado que tenemos una transacción distribuida con más de una sesión involucrada de alguna manera con la misma transacción.

MARS (Conjuntos de resultados activos múltiples)

¿Qué pasa con el uso de conjuntos de resultados activos múltiples (MARS)? ¿También esperaríamos ver TRANSACTION_MUTEX acumulan cuando se asocian con el uso de MARS?

Para esto, utilicé el siguiente código de aplicación de consola C# probado desde Microsoft Visual Studio contra mi instancia de SQL Server 2012 y la base de datos de crédito. La lógica de lo que realmente estoy haciendo no es muy útil (devuelve una fila de cada tabla), pero los lectores de datos están en la misma conexión y el atributo de conexión MultipleActiveResultSets se establece en verdadero, por lo que fue suficiente para verificar si MARS podría conducir TRANSACTION_MUTEX acumulación también:

string ConnString = @"Server=.;Database=Credit;Trusted_Connection=True;MultipleActiveResultSets=true;";
SqlConnection MARSCon = new SqlConnection(ConnString);
 
MARSCon.Open();
 
SqlCommand MARSCmd1 = new SqlCommand("SELECT payment_no FROM dbo.payment;", MARSCon);
SqlCommand MARSCmd2 = new SqlCommand("SELECT charge_no FROM dbo.charge;", MARSCon);
 
SqlDataReader MARSReader1 = MARSCmd1.ExecuteReader();
SqlDataReader MARSReader2 = MARSCmd2.ExecuteReader();
 
MARSReader1.Read();
MARSReader2.Read();
 
Console.WriteLine("\t{0}", MARSReader1[0]);
Console.WriteLine("\t{0}", MARSReader2[0]);

Después de ejecutar este código, vi la siguiente acumulación de TRANSACTION_MUTEX :

wait_type            waiting_tasks_count   wait_time_ms   max_wait_time_ms   signal_wait_time_ms
TRANSACTION_MUTEX    8                     2              0                  0

Entonces, como puede ver, la actividad de MARS (aunque se haya implementado de manera trivial) provocó un aumento en TRANSACTION_MUTEX acumulación de tipo de espera. Y el atributo de cadena de conexión en sí mismo no impulsa esto, lo hace la implementación real. Por ejemplo, eliminé la implementación del segundo lector y solo mantuve un único lector con MultipleActiveResultSets=true , y como era de esperar, no hubo TRANSACTION_MUTEX acumulación de tiempo de espera.

Conclusión

Si está viendo un nivel alto de TRANSACTION_MUTEX esperas en su entorno, espero que esta publicación le brinde una idea de tres vías para explorar:para determinar de dónde provienen estas esperas y si son necesarias o no.


No