¿Cuáles son las principales causas de puntos muertos y pueden prevenirse?


55

Recientemente, una de nuestras aplicaciones ASP.NET mostró un error de bloqueo de la base de datos y se me solicitó que verificara y corrigiera el error. Logré encontrar que la causa del punto muerto era un procedimiento almacenado que actualizaba rigurosamente una tabla dentro de un cursor.

Esta es la primera vez que veo este error y no sabía cómo rastrearlo y solucionarlo de manera efectiva. ¡Intenté todas las formas posibles que conozco y finalmente descubrí que la tabla que se está actualizando no tiene una clave principal! Afortunadamente era una columna de identidad.

Más tarde encontré al desarrollador que creó una base de datos con script para la implementación en mal estado. Agregué una clave principal y el problema se resolvió.

Me sentí feliz y volví a mi proyecto, e investigué un poco para descubrir la razón de ese punto muerto ...

Aparentemente, fue una condición de espera circular que causó el punto muerto. Las actualizaciones aparentemente tardan más sin una clave primaria que con la clave primaria.

Sé que no es una conclusión bien definida, es por eso que estoy publicando aquí ...

  • ¿La clave primaria que falta es el problema?
  • ¿Existen otras condiciones que causen un punto muerto que no sea (exclusión mutua, retención y espera, sin preferencia y espera circular)?
  • ¿Cómo puedo prevenir y rastrear puntos muertos?

2
La mayoría de IME (¿todos?) De los puntos muertos que he visto suceden debido a esperas circulares (principalmente debido al uso excesivo de desencadenantes).
Sathyajith Bhat

La circularidad es una de las condiciones necesarias de un punto muerto. Puede evitar cualquier punto muerto si todas sus sesiones adquieren bloqueos en el mismo orden.
Peter G.

Respuestas:


38

rastrear puntos muertos es el más fácil de los dos:

Por defecto, los puntos muertos no se escriben en el registro de errores. Puede hacer que SQL escriba puntos muertos en el registro de errores con las marcas de seguimiento 1204 y 3605.

Escriba información de punto muerto en el registro de errores de SQL Server: DBCC TRACEON (-1, 1204, 3605)

Desactívelo: DBCC TRACEOFF (-1, 1204, 3605)

Consulte "Solución de problemas de puntos muertos" para obtener información sobre el indicador de traza 1204 y el resultado que obtendrá cuando esté activado. https://msdn.microsoft.com/en-us/library/ms178104.aspx

La prevención es más difícil, esencialmente debes tener en cuenta lo siguiente:

El bloque de código 1 bloquea el recurso A, luego el recurso B, en ese orden.

El bloque de código 2 bloquea el recurso B, luego el recurso A, en ese orden.

Esta es la condición clásica en la que puede ocurrir un punto muerto, si el bloqueo de ambos recursos no es atómico, el Bloque de código 1 puede bloquear a A y ser adelantado, luego el Bloque de código 2 bloquea B antes de que A recupere el tiempo de procesamiento. Ahora tienes un punto muerto.

Para prevenir esta condición, puede hacer algo como lo siguiente

Bloque de código A (código psuedo)

Lock Shared Resource Z
    Lock Resource A
    Lock Resource B
Unlock Shared Resource Z
...

Bloque de código B (pseudocódigo)

Lock Shared Resource Z
    Lock Resource B
    Lock Resource A
Unlock Shared Resource Z
...

sin olvidar desbloquear A y B cuando haya terminado con ellos

esto evitaría el bloqueo entre el bloque de código A y el bloque de código B

Desde la perspectiva de la base de datos, no estoy seguro de cómo evitar esta situación, ya que la base de datos maneja los bloqueos, es decir, los bloqueos de fila / tabla al actualizar los datos. Donde he visto que ocurren más problemas es donde viste el tuyo, dentro de un cursor. Los cursores son notoriamente ineficientes, evítelos si es posible.


¿Querías bloquear el recurso A antes que el recurso B en el bloque de código B? Tal como está escrito, esto provocará puntos muertos ... como usted mismo menciona en los comentarios anteriores. En la medida de lo posible, debe bloquear los recursos en el mismo orden siempre, incluso si necesita consultas ficticias al principio para garantizar ese orden de bloqueo.
Gerard ONeill

23

mis artículos favoritos para leer y aprender sobre puntos muertos son: Charla simple : localizar puntos muertos y SQL Server Central: usar Profiler para resolver puntos muertos . Le darán muestras y consejos sobre cómo manejar una situación difícil.

En resumen, para resolver un problema actual, acortaría las transacciones involucradas, sacaría la parte innecesaria de ellas, me ocuparía del orden de uso de los objetos, vería qué nivel de aislamiento es realmente necesario, no leeré innecesariamente datos...

Pero mejor lea los artículos, serán mucho más amables en los consejos.


16

A veces, un punto muerto se puede resolver agregando indexación, ya que permite que la base de datos bloquee registros individuales en lugar de toda la tabla, por lo que reduce la contención y la posibilidad de que las cosas se atasquen.

Por ejemplo, en InnoDB :

Si no tiene índices adecuados para su declaración y MySQL debe escanear toda la tabla para procesar la declaración, cada fila de la tabla se bloquea, lo que a su vez bloquea todas las inserciones de otros usuarios en la tabla. Es importante crear buenos índices para que sus consultas no escaneen innecesariamente muchas filas.

Otra solución común es desactivar la coherencia transaccional cuando no es necesario, o cambiar su nivel de aislamiento , por ejemplo, un trabajo de larga duración para calcular estadísticas ... una respuesta cercana generalmente es suficiente, no necesita números precisos, a medida que cambian debajo de ti. Y si tarda 30 minutos en completarse, no desea que detenga todas las demás transacciones en esas tablas.

...

En cuanto a su seguimiento, depende del software de base de datos que esté utilizando.


Es una cortesía común proporcionar comentarios cuando se vota hacia abajo ... Esta es una respuesta válida, una declaración de selección que se actualiza a un bloqueo de tabla y demora para siempre seguramente puede causar un punto muerto.
BlackICE

1
MS SQLServer también puede proporcionar un comportamiento de bloqueo inesperado si los índices no están agrupados. Ignorará en silencio su dirección para usar el bloqueo de nivel de fila y bloqueará el nivel de página. A continuación, puede obtener puntos muertos que esperan en la página.
Jay

7

Solo para desarrollar en el cursor. de hecho es realmente malo. Bloquea toda la tabla y luego procesa las filas una por una.

Es mejor recorrer las filas a la manera de un cursor usando un ciclo while

En el ciclo while, se realizará una selección para cada fila del ciclo y el bloqueo se producirá en una sola fila a la vez. El resto de los datos en la tabla es gratuito para consultas, lo que reduce las posibilidades de que ocurra un punto muerto.

Además es más rápido. Hace que te preguntes por qué hay cursores de todos modos.

Aquí hay un ejemplo de este tipo de estructura:

DECLARE @LastID INT = (SELECT MAX(ID) FROM Tbl)
DECLARE @ID     INT = (SELECT MIN(ID) FROM Tbl)
WHILE @ID <= @LastID
    BEGIN
    IF EXISTS (SELECT * FROM Tbl WHERE ID = @ID)
        BEGIN
        -- Do something to this row of the table
        END

    SET @ID += 1  -- Don't forget this part!
    END

Si su campo de ID es escaso, es posible que desee obtener una lista separada de ID e iterar a través de eso:

DECLARE @IDs TABLE
    (
    Seq INT NOT NULL IDENTITY PRIMARY KEY,
    ID  INT NOT NULL
    )
INSERT INTO @IDs (ID)
    SELECT ID
    FROM Tbl
    WHERE 1=1  -- Criteria here

DECLARE @Rec     INT = 1
DECLARE @NumRecs INT = (SELECT MAX(Seq) FROM @IDs)
DECLARE @ID      INT
WHILE @Rec <= @NumRecs
    BEGIN
    SET @ID = (SELECT ID FROM @IDs WHERE Seq = @Seq)

    -- Do something to this row of the table

    SET @Seq += 1  -- Don't forget this part!
    END

6

Falta una clave principal no es el problema. Al menos por sí mismo. Primero, no necesita un primario para tener índices. En segundo lugar, incluso si está haciendo escaneos de tabla (lo que tiene que suceder si su consulta particular no está utilizando un índice, un bloqueo de tabla no provocará un punto muerto por sí solo. Un proceso de escritura esperaría una lectura, y un proceso de lectura haría esperar un escrito, y por supuesto las lecturas no tendrían que esperar el uno para el otro.

Además de las otras respuestas, el nivel de aislamiento de la transacción es importante, ya que las lecturas repetidas y serializadas son las que hacen que los bloqueos de 'lectura' se mantengan hasta el final de la transacción. Bloquear un recurso no causa un punto muerto. Mantenerlo cerrado lo hace. Las operaciones de escritura siempre mantienen sus recursos bloqueados hasta el final de la transacción.

Mi estrategia de prevención de bloqueo favorita es usar las funciones de 'instantánea'. La función de lectura de confirmación de lectura significa que las lecturas no usan bloqueos. Y si necesita más control que 'Lectura comprometida', existe la función 'Nivel de aislamiento de instantánea'. Este permite que se produzca una transacción serializada (usando los términos de MS aquí) sin bloquear a los otros jugadores.

Por último, se puede evitar una clase de puntos muertos utilizando un bloqueo de actualización. Si lee y mantiene la lectura (RETENER, o usa Lectura repetible), y otro proceso hace lo mismo, entonces ambos intentan actualizar los mismos registros, tendrá un punto muerto. Pero si ambos solicitan un bloqueo de actualización, el segundo proceso esperará al primero, mientras permite que otros procesos lean los datos usando bloqueos compartidos hasta que los datos se escriban realmente. Por supuesto, esto no funcionará si uno de los procesos aún solicita un bloqueo HOLD compartido.


-2

Si bien los cursores son lentos en SQL Server, puede evitar el bloqueo en un cursor tirando de los datos de origen para el cursor en una tabla Temp y ejecutando el cursor sobre él. Esto evita que el cursor bloquee la tabla de datos real y los únicos bloqueos que obtienes son para las actualizaciones o inserciones realizadas dentro del cursor que solo se mantienen durante la duración de la inserción / actualización y no durante la duración del cursor.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.