La sugerencia de Tablock desencadena puntos muertos


10

Estaba insertando dos conjuntos de datos, utilizando un registro mínimo, en una tabla de montón vacía mediante dos tareas Ejecutar SQL que se ejecutan en paralelo y con SQL de la siguiente forma.

INSERT INTO Table (TABLOCK) SELECT FROM ...

Después de que el trabajo se bloquea un poco, una de las tareas de SQL se convirtió en una víctima de punto muerto. A continuación se muestra la salida XML del gráfico de punto muerto.

¿Alguien puede explicar lo que estaba sucediendo debajo del capó?

  <resource-list>
   <objectlock lockPartition="0" objid="1586156746" subresource="FULL" dbid="7" objectname="dbo.TargetTable" id="lock7374a00" mode="IX" associatedObjectId="1586156746">
    <owner-list>
     <owner id="process9609dc8" mode="Sch-S"/>
     <owner id="process9609dc8" mode="IX"/>
    </owner-list>
    <waiter-list>
     <waiter id="process5e13048" mode="X" requestType="convert"/>
    </waiter-list>
   </objectlock>
   <objectlock lockPartition="0" objid="1586156746" subresource="FULL" dbid="7" objectname="dbo.TargetTable" id="lock7374a00" mode="IX" associatedObjectId="1586156746">
    <owner-list>
     <owner id="process5e13048" mode="Sch-S"/>
     <owner id="process5e13048" mode="IX"/>
    </owner-list>
    <waiter-list>
     <waiter id="process9609dc8" mode="X" requestType="convert"/>
    </waiter-list>
   </objectlock>
  </resource-list>

Las cosas se ponen mucho más complicadas porque descubrí que, en la mayoría de los casos, las dos tareas Ejecutar SQL pueden ejecutarse en paralelo con éxito. Prueba a continuación:

Create table dbo.TablockInsert (c1 int, c2 int, c3 int)

--then issue the script in two Execute Sql Task in parallel you won't fail:
insert into dbo.TablockInsert(TABLOCK) SELECT 1, 1, 1

Dado que la única diferencia es la instrucción SELECT ... FROM ..., ¿parece que la instrucción SELECT ... FROM ... puede tener un impacto en el modo de bloqueo aquí?


Puede especificar TABLOCKX en lugar de TABLOCK para evitar el punto muerto. Aunque eso también serializaría el acceso a la tabla, aún obtendría un registro mínimo.
Dan Guzman

Respuestas:


8

La Guía de rendimiento de carga de datos se escribió para SQL Server 2008, pero por lo que puedo decir, Microsoft no ha realizado ninguna mejora en esta área para los montones. Aquí hay una cotización para su escenario de carga:

Carga masiva de una tabla vacía, no particionada

La carga de datos en una tabla no particionada, aunque es una operación simple, se puede optimizar de varias maneras.

...

Las operaciones de inserción múltiples y concurrentes para montones solo son posibles cuando el método masivo elegido emite bloqueos de actualización masiva (BU) en la tabla. Dos bloqueos de actualización masiva (BU) son compatibles y, por lo tanto, pueden ejecutarse dos operaciones masivas al mismo tiempo.

En este escenario, tanto INSERT ... SELECT como SELECT INTO tienen un inconveniente. Ambas operaciones toman un bloqueo exclusivo (X) a nivel de tabla en el destino. Esto significa que solo una operación de carga masiva puede ejecutarse en un momento dado, lo que limita la escalabilidad. Sin embargo, BCP, BULK INSERT e Integration Services son capaces de tomar bloqueos de actualización masiva (BU), si especifica la sugerencia TABLOCK.

La parte importante es que no obtienes un bloqueo BU con INSERT ... SELECT. Siempre obtendrá un bloqueo exclusivo en la mesa, por lo que solo uno INSERTpuede correr a la vez.

En los comentarios, dijo que insertará 100k filas o menos y que otros procesos no se ejecutarán en las tablas durante las inserciones. Al enviar dos consultas INSERT a la base de datos, esperaría que suceda una de estas tres cosas:

  1. Una inserción se ejecuta primero y bloquea la otra inserción. El segundo inserto espera hasta que se realiza el primer inserto.
  2. Una inserción termina antes de que comience la segunda inserción. No hay bloqueo explícito pero no se ejecutan simultáneamente.
  3. Obtienes un punto muerto y solo una inserción se completa con éxito.

En todos los casos, usted se beneficia o no se ve perjudicado al agregar una TABLOCKXpista a la consulta, por lo que esa es mi recomendación de evitar el punto muerto. Si quieres saber por qué a veces ocurre el punto muerto, tendrás que buscar otra respuesta para eso.

En un escenario diferente en el que realmente necesita una inserción paralela, dos formas de solucionar el problema de la BU son particionar su montón y hacer que cada sesión se inserte en una partición separada o cargar sus datos a través de BCP, BULK INSERT o Integration Services .


Gracias por la respuesta, pero la situación que encontré en punto muerto es el único caso que tengo por ahora. Para la mayoría de los casos cuando emite INSERT INTO WITH (TABLOCK) SELECT FROM en paralelo, el trabajo no fallará. Por cierto, estoy usando SQL SERVER 2008 R2. He agregado un ejemplo exitoso en mi pregunta.
SqlWhale

@tec en los casos en que no falla, presumiblemente terminan ejecutándose en serie. Tal vez solo llegue al problema cuando haya un cierto tiempo de inicio, por ejemplo, para la compilación del plan.
Martin Smith

@MartinSmith La consulta en la que encontré el problema en el proyecto es mucho más compleja que el ejemplo SELECT 1, que requiere más sobrecarga de compilación pero solo está leyendo un par de tablas. Estoy tratando de reproducir este tipo de punto muerto mediante consultas más complejas.
SqlWhale

@TecKnowNothing ¿Aproximadamente cuántas filas inserta? ¿Cuántas veces al día se ejecuta el proceso? ¿Se seleccionan otras consultas de la tabla mientras se cargan los datos?
Joe Obbish

@JoeObbish 1. No creo que el número de filas sea un problema aquí, solo tenemos 4000 - 70000 para cada consulta y son datos altamente agregados para el uso del cubo. 2. Todavía está en etapa de prueba, se supone que se ejecuta una vez al día. 3. Nada más está leyendo de la tabla de destino.
SqlWhale

4

Se está insertando dbo.TargetTabledesde dos sesiones y ambas utilizando una TABLOCKsugerencia. Tanto la retención process9609dc8como el process5e13048proceso Sch-Sy los IXbloqueos que son compatibles entre sí para que ambos procesos puedan mantenerse al mismo tiempo. Pero ambos quieren convertir el IXbloqueo al Exclusive Xtipo. XLas cerraduras no son compatibles entre sí. Por lo tanto, el servidor SQL eligió una de las sesiones como víctima de punto muerto en lugar de esperar infinitamente el uno al otro.

Información básica de bloqueo.

Gráfico de compatibilidad de bloqueos (motor de base de datos).

Detección y finalización de puntos muertos.

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.