Punto muerto de actualización del índice de SQL Server
13
Tengo 2 consultas que cuando se ejecutan al mismo tiempo causan un punto muerto.
Consulta 1: actualice una columna que se incluye en un índice (index1):
update table1 set column1 = value1 where id =@Id
Toma X-Lock en la tabla 1 y luego intenta un X-Lock en index1.
Consulta 2:
select columnx, columny, etc from table1 where{some condition}
Toma un S-Lock en index1 y luego intenta un S-Lock en table1.
¿Hay alguna manera de evitar el punto muerto mientras se mantienen las mismas consultas? Por ejemplo, ¿puedo de alguna manera tomar un X-Lock en el índice en la transacción de actualización antes de la actualización para asegurar que la tabla y el acceso al índice estén en el mismo orden, lo que debería evitar el punto muerto?
El nivel de aislamiento es lectura confirmada. Los bloqueos de fila y página están habilitados para los índices. Es posible que el mismo registro esté participando en ambas consultas: no puedo distinguirlo del gráfico de punto muerto ya que no muestra los parámetros.
¿Hay alguna manera de evitar el punto muerto mientras se mantienen las mismas consultas?
El gráfico de punto muerto muestra que este punto muerto en particular era un punto muerto de conversión asociado con una búsqueda de marcadores (una búsqueda RID en este caso):
Como señala la pregunta, el riesgo general de punto muerto surge porque las consultas pueden obtener bloqueos incompatibles en los mismos recursos en diferentes órdenes. La SELECTconsulta debe acceder al índice antes de la tabla debido a la búsqueda de RID, mientras que la UPDATEconsulta primero modifica la tabla y luego el índice.
Eliminar el punto muerto requiere eliminar uno de los ingredientes del punto muerto. Las siguientes son las principales opciones:
Evite la búsqueda de RID haciendo que el índice no agrupado cubra. Probablemente esto no sea práctico en su caso porque la SELECTconsulta devuelve 26 columnas.
Evite la búsqueda de RID creando un índice agrupado. Esto implicaría crear un índice agrupado en la columna Proposal. Vale la pena considerar esto, aunque parece que esta columna es de tipo uniqueidentifier, lo que puede o no ser una buena opción para un índice agrupado, dependiendo de cuestiones más amplias.
Evitar tomar bloqueos compartidos cuando se lee al permitir que los READ_COMMITTED_SNAPSHOTo SNAPSHOTde bases de datos opciones. Esto requeriría una prueba cuidadosa, especialmente con respecto a cualquier comportamiento de bloqueo diseñado. El código de activación también requeriría pruebas para garantizar que la lógica funcione correctamente.
Evite tomar bloqueos compartidos al leer utilizando el READ UNCOMMITTEDnivel de aislamiento para la SELECTconsulta. Se aplican todas las advertencias habituales.
Evite la ejecución concurrente de las dos consultas en cuestión mediante el uso de un bloqueo exclusivo de la aplicación (consulte sp_getapplock ).
Use sugerencias de bloqueo de tabla para evitar la concurrencia. Este es un martillo más grande que la opción 5, ya que puede afectar otras consultas, no solo las dos identificadas en la pregunta.
¿De alguna manera puedo tomar un X-Lock en el índice en la transacción de actualización antes de la actualización para asegurar que la tabla y el acceso al índice estén en el mismo orden
Puede probar esto, envolviendo la actualización en una transacción explícita y realizar una SELECTcon una XLOCKpista sobre el valor del índice no agrupado antes de la actualización. Esto depende de que usted sepa con certeza cuál es el valor actual en el índice no agrupado, que obtenga el plan de ejecución correcto y que anticipe correctamente todos los efectos secundarios de tomar este bloqueo adicional. También se basa en que el motor de bloqueo no es lo suficientemente inteligente como para evitar tomar el bloqueo si se considera redundante .
En resumen, si bien esto es factible en principio, no lo recomiendo. Es demasiado fácil perderse algo, o burlarse de uno mismo de manera creativa. Si realmente debe evitar estos puntos muertos (en lugar de solo detectarlos y volver a intentarlos), le animo a que busque en su lugar las soluciones más generales enumeradas anteriormente.
Si profundizamos en el tema, creo que dejarlo sin cambios probablemente sea lo mejor. Es un problema más común del que me di cuenta originalmente.
Dale K
1
Tengo un problema similar que ocurre ocasionalmente y aquí está el enfoque que tomo.
Añadir set deadlock priority low;a la selección. Esto hará que esta consulta sea la víctima del punto muerto cuando se produce un punto muerto.
Configure la lógica de reintento dentro de su aplicación para reintentar automáticamente la selección si falla debido a un punto muerto (o tiempo de espera), después de esperar / dormir durante un breve período de tiempo para permitir que se completen las consultas de bloqueo.
Nota: si selectes parte de una transacción explícita de múltiples declaraciones, entonces debe asegurarse de volver a intentar toda la transacción, y no solo la declaración que falló, o de lo contrario puede obtener algunos resultados inesperados. Si se trata de un solo selectentonces está bien, pero si se trata de la declaración xde ndentro de una transacción, a continuación, sólo asegúrese de que vuelva a intentar todas las ndeclaraciones durante el reintento.
Tengo un problema similar que ocurre ocasionalmente y aquí está el enfoque que tomo.
set deadlock priority low;
a la selección. Esto hará que esta consulta sea la víctima del punto muerto cuando se produce un punto muerto.Nota: si
select
es parte de una transacción explícita de múltiples declaraciones, entonces debe asegurarse de volver a intentar toda la transacción, y no solo la declaración que falló, o de lo contrario puede obtener algunos resultados inesperados. Si se trata de un soloselect
entonces está bien, pero si se trata de la declaraciónx
den
dentro de una transacción, a continuación, sólo asegúrese de que vuelva a intentar todas lasn
declaraciones durante el reintento.fuente