2009-05-08 16 views
5

Tengo una aplicación Java que está realizando múltiples operaciones simultáneas CRUD en una base de datos. Estoy agregando soporte para SQLServer pero estoy teniendo problemas con el bloqueo durante las eliminaciones concurrentes. Después de algunas investigaciones, parece que el problema puede deberse a la escalada de bloqueo en una tabla en particular.Interbloqueo SQLServer

En un intento de solucionarlo, decidí hacer todas las lecturas sobre la tabla en cuestión para "actualizar" usando la sugerencia UPDLOCK para evitar el interbloqueo. Sin embargo, sigo viendo el problema. He habilitado el seguimiento en SQL Server y he encontrado la siguiente traza de interbloqueo en los registros de SQLServer:

punto muerto encontrado .... Impresión de información de interbloqueo Esperar por el gráfico

Nodo: 1 CLAVE: 5: 72057594042384384 (54048e7b3828) CleanCnt: 3 Modo: X Flags: 0x0 Grant List 1: Propietario: 0x03D08C40 Modo: X Flg: 0x0 Ref: 0 Vida: 02000000 SPID: 62 ECID: 0 XactLockInfo: 0x04834274 SPID: 62 ECID: 0 Statement Tipo: DELETE Número de línea: 1 Buf de entrada: Evento de idioma: (@ P0 nvarchar (4000)) eliminar de part_data donde part_id = @ P0 Solicitado por: restype: LockOwner Stype: 'OR'Xdes: Modo 0x04B511C8: U SPID: 60 BatchID: 0 ECID: 0 TaskProxy: (0x058BE378) Valor: 0x3d08500 Coste: (0/1296)

Node: 2

CLAVE: 5: 72057594042384384 (f903d6d6e0ac) CleanCnt: 2 Modo: Banderas X: 0x0 subvención Lista 0: propietario: Modo 0x03D088A0: X Flg: 0x0 Ref: 0 vida: 02000000 SPID: 60 ECID: 0 XactLockInfo: 0x04B511EC SPID: 60 ECID: 0 Tipo de extracto: DELETE Número de línea: 1 Buf de entrada: Evento de idioma: (@ P0 nvarchar (4000)) eliminar de part_data donde part_id = @ P0 Solicitado por: ResType: LockOwner Stype: 'OR'Xdes: 0x04834250 Modo: U SPID: 62 BatchID: 0 ECID: 0 TaskProxy :(0x047BA378) Valor: Costo 0x3d089e0: (0/4588)

propietario del recurso víctima: restype: LockOwner Stype: 'OR'Xdes: Modo 0x04B511C8: T SPID: 60: 0 BATCHID ECID: 0 TaskProxy: (0x058BE378) Valor : 0x3d08500 Costo: (0/1296)

El perfilador SQLServer muestra esto como dos clientes que tienen bloqueos de actualización (U) e intentan escalar a bloqueos exclusivos (X). Los documentos SQLServer que he leído dicen que solo un cliente puede tener un bloqueo (U) en una tabla en un momento dado, así que me pregunto por qué veo la situación mostrada en el rastreo.

El objeto de base de datos a que se hace referencia en ese rastreo es un índice en una clave externa. Si alguien con experiencia en solucionar este tipo de problemas pudiera ofrecer consejos, sería una gran ayuda.

Gracias, Brad.

EDITAR añadido estancamiento gráfico XML conforme a lo solicitado:

<deadlock-list> 
<deadlock victim="process989018"> 
    <process-list> 
    <process id="process6aa7a8" taskpriority="0" logused="4844" waitresource="KEY: 5:72057594042384384 (5504bdfb7529)" waittime="9859" ownerId="613553" transactionname="implicit_transaction" lasttranstarted="2009-05-08T11:52:39.137" XDES="0x5fcbc30" lockMode="U" schedulerid="1" kpid="3516" status="suspended" spid="59" sbid="0" ecid="0" priority="0" transcount="2" lastbatchstarted="2009-05-08T11:52:39.183" lastbatchcompleted="2009-05-08T11:52:39.183" clientapp="jTDS" hostname="LOIRE" hostpid="123" loginname="sa" isolationlevel="read committed (2)" xactid="613553" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058"> 
    <executionStack> 
    <frame procname="adhoc" line="1" stmtstart="40" sqlhandle="0x0200000007c76c39efdd8317c6fa7b611b4fd958f05cfcf4"> 
delete from part_data where part_id = @P0  </frame> 
    </executionStack> 
    <inputbuf>(@P0 nvarchar(4000))delete from part_data where part_id = @P0</inputbuf> 
    </process> 
    <process id="process989018" taskpriority="0" logused="1528" waitresource="KEY: 5:72057594042384384 (5e0405cb0377)" waittime="1250" ownerId="613558" transactionname="implicit_transaction" lasttranstarted="2009-05-08T11:52:39.183" XDES="0x48318f0" lockMode="U" schedulerid="2" kpid="2692" status="suspended" spid="60" sbid="0" ecid="0" priority="0" transcount="2" lastbatchstarted="2009-05-08T11:52:39.183" lastbatchcompleted="2009-05-08T11:52:39.183" clientapp="jTDS" hostname="LOIRE" hostpid="123" loginname="sa" isolationlevel="read committed (2)" xactid="613558" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058"> 
    <executionStack> 
    <frame procname="adhoc" line="1" stmtstart="40" sqlhandle="0x0200000007c76c39efdd8317c6fa7b611b4fd958f05cfcf4"> 
delete from part_data where part_id = @P0  </frame> 
    </executionStack> 
    <inputbuf>(@P0 nvarchar(4000))delete from part_data where part_id = @P0</inputbuf> 
    </process> 
    </process-list> 
    <resource-list> 
    <keylock hobtid="72057594042384384" dbid="5" objectname="MESSAGESTOREDB61.dbo.part_data" indexname="idx_part_data_part_id" id="lock3cab740" mode="X" associatedObjectId="72057594042384384"> 
    <owner-list> 
    <owner id="process6aa7a8" mode="X"/> 
    </owner-list> 
    <waiter-list> 
    <waiter id="process989018" mode="U" requestType="wait"/> 
    </waiter-list> 
    </keylock> 
    <keylock hobtid="72057594042384384" dbid="5" objectname="MESSAGESTOREDB61.dbo.part_data" indexname="idx_part_data_part_id" id="lock3cad340" mode="X" associatedObjectId="72057594042384384"> 
    <owner-list> 
    <owner id="process989018" mode="X"/> 
    </owner-list> 
    <waiter-list> 
    <waiter id="process6aa7a8" mode="U" requestType="wait"/> 
    </waiter-list> 
    </keylock> 
    </resource-list> 
</deadlock> 
</deadlock-list> 
+0

¿Tiene una cascada de eliminar en algún lugar? –

+0

Hola Stefan. No, no tengo ninguna restricción de eliminación en cascada. – Brad

+0

formato de pantalla web de no importa, copie/pegue en un archivo .XML local y ábralo y se verá bien –

Respuesta

0

Estoy asumiendo que se ejecutó algo como: DBCC TRACEON (1222, -1) "y/o" DBCC TRACEON (1204, -1) . Encuentro difícil de leer la traza del punto muerto en los registros de SQLServer. ¿Estás seguro de que es un intento de escalar a bloqueos exclusivos (X)?

Intente ejecutar la ejecución de un rastreo en el generador de perfiles (seleccione la plantilla en blanco), seleccione el "evento de gráfico de interbloqueo" y en la nueva pestaña que aparece (Configuración de extracción de eventos), guarde cada uno (marque "salvar deadlock XML events" por separado ") en su propio archivo.Abra este archivo en un visor xml y será fácil saber qué está sucediendo. Cada proceso está contenido, con una pila de ejecución de llamadas a procedimiento/desencadenador/etc., y todos los bloqueos también están ahí. Me resulta difícil creer que los dos DELETE donde col = @ value están causando el problema, mire la pila de ejecución, ¿hay un disparador u otra cosa que está pasando?

Mire la sección "lista de recursos" del archivo, que mostrará qué está bloqueado y retenido por cada proceso que causa el interbloqueo. Descubre cómo no bloquear uno de ellos y se resolverá el punto muerto.

+0

Hola, gracias por comentar. He usado el generador de perfiles anteriormente, pero ahora puedo ver que estaba interpretando el resultado incorrectamente. De la lista de recursos XML, parece que los dos clientes ya tienen (X) bloqueos y de hecho están solicitando bloqueos (U). Desde la pila de ejecución, lo único que se muestra son las dos instrucciones de eliminación. – Brad

+0

¿cómo puede eliminar lo que intenta hacer en cada proceso? ¿múltiple o simplemente el único para cada proceso? Si está realizando varias eliminaciones en un bucle (con una transacción) en cada proceso, entonces puede tener problemas de bloqueo porque los bloqueos no están en las filas, sino en las páginas de varias filas. edite su pregunta e incluya el gráfico de interbloqueo xml para que pueda ver mejor lo que está viendo. –

+0

La transacción está realizando una eliminación en un gráfico de objetos que se combinan para formar una unidad lógica de datos. La tabla sobre la que falla la eliminación corresponde al objeto situado más abajo en este gráfico. Es probable que haya más de uno de estos objetos, pero una eliminación solo se realiza una vez en este nivel, ya que es un "eliminar donde la clave externa =?". Todavía tengo curiosidad de por qué el punto muerto está ocurriendo en el índice en lugar de la mesa en sí. – Brad

2

bloqueos en SQLServer casi siempre se originan en el hecho de que un solo hilo intenta escribir y leer utilizando dos conexiones y, por lo tanto, dos transacciones. Si desea que esto funcione, realice todas las operaciones en un solo subproceso usando UNA conexión y asegúrese de que está realmente reutilizando la conexión. Puede ser debido a la estratificación en su aplicación que está utilizando accidentalmente una conexión diferente para leer durante una transacción que hace que la lectura espere a que el otro código (usando otra conexión) se complete, lo que nunca ocurre ya que la lectura nunca termina.

Ejemplo (pseudo pasos)

empezar

  • datos trans de escritura (por ejemplo, INSERT, UPDATE) para Foo tabla
  • leer algunos datos (que desencadena exploración de tabla) de Foo usando diferente conexión MUELLE DEADLOCK, como nunca se lee, por lo que transactio nunca se compromete
+0

Frans, ese es un punto interesante. El material de JDBC se realiza a través de la plantilla Spring JDBC, así que no estoy manejando ninguna conexión, pero es posible que una lectura se realice dentro de una transacción junto a una actualización o eliminación.Voy a volver y buscar el tipo de situación que describió – Brad

+0

Después de algunas investigaciones adicionales, he notado que una de las transacciones sospechosas tiene un inserto mezclado con las eliminaciones. Mi aplicación utiliza cuatro clases de servicio para el acceso a la base de datos que agrupan las operaciones como lecturas, actualizaciones, inserciones y eliminaciones. Supongo que el problema podría ser que la inserción utiliza una conexión diferente a las eliminaciones ya que está en una clase de plantilla JDBC separada. Pensé que la gestión de transacciones de Spring obligaría a las operaciones a usar una sola conexión, ¿quizás estoy equivocado? – Brad

+0

Nunca he trabajado con Spring, así que no tengo idea de si reutiliza una transacción en progreso, podría imaginar que lo haría, pero también podría imaginar que no lo haría (si, por ejemplo, se conecta a una base de datos diferente, TIENE QUE CREAR uno nuevo) –

4

Bienvenido a horrible.

La última vez que me encontré con una situación como esta fue porque una actualización o una eliminación no pudo encontrar un buen índice que lo ayudara a aislar las filas a las que afectaba. Esto causó una escalada errática de bloqueo ya que utilizaba un índice que no abarcaba para modificar la ubicación de los registros.

Por lo tanto, si puede aislar algunas de las consultas, consulte el sql para ver si no puede experimentar proporcionando algunos índices de cobertura.

Un índice de cobertura es un índice que incluye todos los campos en la cláusula where específica.

+0

No hay índice de cobertura que ayude, pero un buen índice de cobertura incluye todas las columnas que se deben devolver. Si selecciona una cola, colb de table1 ... cola y colb deben estar en el índice de cobertura para que SQL Server no tenga que volver a la fila original para obtener los datos. –

0

... decidí hacer todas las lecturas sobre la mesa en cuestión puede hacer "actualización" utilizando la sugerencia UPDLOCK para que el estancamiento podría evitarse ...

... Sí, se ha declarado una transacción a para asegurarse de que el gráfico completo del objeto esté eliminado o dejado intacto si hubiera un error en . No hay otras bases de datos operaciones en la transacción, algunos lecturas se hacen antes de que se inicie la transacción pero ninguno dentro de él ...

no estoy seguro de entender el razonamiento detrás de la escalada de los bloqueos de lectura fuera la transacción, yo pensaría que esto solo agravaría el problema. Mi experiencia ha sido que especificar indirectas de bloqueo preventivamente causa más daño que bien.

Dicho esto ...

Parece que está intentando eliminar varias filas dentro de cada transacción y que el bloqueo está ocurriendo porque SQL está escalando el nivel de bloqueo en la tabla para cada una. Recuerde que en SQL Server Row, Key-Range y Page bloqueos escalan de inmediato a Bloqueos de tabla. Si tiene varias transacciones eliminando múltiples filas, intentarán escalar y obtendrá bloqueos.

Esto matará el rendimiento si tiene muchos usuarios pero intente especificar sugerencias TABLOCK en sus instrucciones de eliminación y vea si el problema desaparece.

También eche un vistazo a los planes de ejecución de sus declaraciones SQL para ver cómo se producen las escaladas de bloqueo.

1

En primer lugar, no use pistas, normalmente SQL Server es mejor dejarlo solo.

Secondo comprueba que no tienes un punto muerto REAL (pero sucede con mucha menos frecuencia que la aparición del punto muerto puede indicar).

En tercer lugar, como alguien sugirió, compruebe si tiene alguna consulta lenta y sintonícela.

En mi experiencia cada vez un cliente ha informado de una situación de bloqueo que esto sucedió porque hubo una consulta lenta ejecución que se intensifica y nunca un punto muerto real y afinar la consulta o la adición de un índice específico siempre resuelto el problema .

No veo de qué versión de SQL Server está hablando, pero cada versión siguiente tiene una mejor gestión de interbloqueo que la anterior y SQL Server 2000 fue particularmente desagradable en este tema.

Saludos
Massimo

+0

Hola Massimo, gracias por tu respuesta. Estoy ocupado en este momento, pero volveré a visitarlo pronto y te responderé. – Brad

0

me encontré con el mismo problema hace muchos años en una mesa targetted por más de mil millones de transacciones por día.

Contamos con cuatro servicios tomcat para manejar las consultas de un sitio web. A veces, dos servicios distintos intentaban tratar la misma consulta, que aparece cuando el tráfico del sitio web era el más bajo. La operación de eliminación de la primera consulta bloqueaba el IX y la segunda consulta, ejecutada al mismo tiempo, hacía lo mismo.

Tratamos ese problema creando una tabla de búfer. Las acciones de eliminación se almacenaron en esa tabla. Cada segundo, un trabajo sql lee esa tabla para eliminar las líneas etiquetadas como "para eliminar".

0

En primer lugar son dos instrucciones de eliminación. Estos dos SPID están ejecutando la misma declaración

delete from part_data where part_id = @P0

y, probablemente, que no será tratando de eliminar el mismo registro. Aquí se produce un interbloqueo porque SQLServer no puede identificar las filas/filas de la tabla part_data con la columna part_id y también bloquea otros registros.

lo que tiene dos opciones

  1. Haga part_id su clave primaria (si es posible)

    o

  2. Alter la declaración de la siguiente manera. (Suponiendo que no hay una clave principal)

    Select primary_key into #temp From part_data where part_id = @p0

    - obtendrá la clave primaria de los registros que se van a eliminar

    delete a from part_data a join #temp b on a.primary_key = b.primary_key

- Puede borre aquellos registros que se eliminarán sin bloquear otros registros

Cuestiones relacionadas