¿Por qué lleva más tiempo crear un índice después de que aumenta el tamaño de la columna?

18

Nuestro proveedor cambió el ancho de las columnas en casi todas las columnas de toda la base de datos. La base de datos es de alrededor de 7 TB, más de 9000 tablas. Estamos tratando de crear un índice en una tabla que tenga 5.5 billones de filas. Antes de la actualización del proveedor, podíamos crear el índice en 2 horas. Ahora lleva días. Lo que han hecho es aumentar cualquier tamaño de varchar (xx) a varchar (256). Entonces, la mayoría de las columnas solían ser varchar (18) o varchar (75), etc.

De todos modos, la clave principal consta de 6 columnas cuyo ancho combinado era de 126 caracteres. Ahora, después de la actualización, la clave principal es de 1283 caracteres, lo que viola el límite de 900 caracteres de los servidores SQL. El ancho total de la columna de la tabla pasó de un recuento total combinado de varchar de 1049 a un recuento total combinado de varchar de 4009.

No hay un aumento en los datos, la tabla no ocupa más "espacio" de lo que lo hacía antes de que aumente todo el ancho de columna, pero el rendimiento para crear algo tan simple como un índice ahora está tomando una cantidad de tiempo irrazonable.

¿Alguien puede explicar por qué lleva tanto tiempo crear e indexar cuando lo único que se hizo fue aumentar el tamaño de las columnas?

El índice que estamos tratando de crear no está agrupado ya que pk es el índice agrupado. Después de varios intentos de crear el índice, nos dimos por vencidos. Creo que funcionó 4 o 5 días sin completarse.

Intenté esto en un entorno que no es de producción al tomar una instantánea del sistema de archivos y poner la base de datos en un servidor más silencioso.

Daniel Miller
fuente

Respuestas:

12

Remus ha señalado útilmente que la longitud máxima de la VARCHARcolumna afecta el tamaño de fila estimado y, por lo tanto, otorga memoria que proporciona SQL Server.

Traté de investigar un poco más para ampliar la parte de su respuesta "de esto en las cosas en cascada". No tengo una explicación completa o concisa, pero esto es lo que encontré.

Repro script

Creé un script completo que genera un conjunto de datos falsos en el que la creación del índice tarda aproximadamente 10 veces más en mi máquina para la VARCHAR(256)versión. Los datos utilizados es exactamente el mismo, pero la primera tabla utiliza las longitudes max reales de 18, 75, 9, 15, 123, y 5, mientras que todas las columnas utilizan una longitud máxima de 256la segunda tabla.


Tecleando la mesa original

Aquí vemos que la consulta original se completa en aproximadamente 20 segundos y las lecturas lógicas son iguales al tamaño de la tabla de ~1.5GB(195K páginas, 8K por página).

-- CPU time = 37674 ms,  elapsed time = 19206 ms.
-- Table 'testVarchar'. Scan count 9, logical reads 194490, physical reads 0
CREATE CLUSTERED INDEX IX_testVarchar
ON dbo.testVarchar (s1, s2, s3, s4)
WITH (MAXDOP = 8) -- Same as my global MAXDOP, but just being explicit
GO


Incrustar la tabla VARCHAR (256)

Para la VARCHAR(256)tabla, vemos que el tiempo transcurrido ha aumentado dramáticamente.

Curiosamente, ni el tiempo de CPU ni las lecturas lógicas aumentan. Esto tiene sentido dado que la tabla tiene exactamente los mismos datos, pero no explica por qué el tiempo transcurrido es mucho más lento.

-- CPU time = 33212 ms,  elapsed time = 263134 ms.
-- Table 'testVarchar256'. Scan count 9, logical reads 194491
CREATE CLUSTERED INDEX IX_testVarchar256
ON dbo.testVarchar256 (s1, s2, s3, s4)
WITH (MAXDOP = 8) -- Same as my global MAXDOP, but just being explicit
GO


Estadísticas de E / S y espera: original

Si capturamos un poco más de detalle (usando p_perfMon, un procedimiento que escribí ), podemos ver que la gran mayoría de las E / S se realizan en el LOGarchivo. Vemos una cantidad relativamente modesta de E / S en el actual ROWS(el archivo de datos principal), y el tipo de espera principal es LATCH_EX, lo que indica la contención de la página en memoria.

También podemos ver que mi disco giratorio está en algún lugar entre "malo" y "sorprendentemente malo", según Paul Randal :)

ingrese la descripción de la imagen aquí


Estadísticas de E / S y espera: VARCHAR (256)

¡Para la VARCHAR(256)versión, las estadísticas de E / S y espera se ven completamente diferentes! Aquí vemos un gran aumento en la E / S en el archivo de datos ( ROWS), y los tiempos de bloqueo ahora hacen que Paul Randal simplemente diga "¡GUAU!".

No es sorprendente que el tipo de espera n. ° 1 sea ahora IO_COMPLETION. Pero, ¿por qué se genera tanta E / S?

ingrese la descripción de la imagen aquí


Plan de consulta real: VARCHAR (256)

Desde el plan de consulta, podemos ver que el Sortoperador tiene un derrame recursivo (¡5 niveles de profundidad!) En la VARCHAR(256)versión de la consulta. (No hay derrame en absoluto en la versión original).

ingrese la descripción de la imagen aquí


Progreso de consultas en vivo: VARCHAR (256)

Podemos usar sys.dm_exec_query_profiles para ver el progreso de la consulta en vivo en SQL 2014+ . En la versión original, todo Table Scany Sortse procesan sin derrames ( spill_page_countpermanece en 0todo momento).

En la VARCHAR(256)versión, sin embargo, podemos ver que los derrames de páginas se acumulan rápidamente para el Sortoperador. Aquí hay una instantánea del progreso de la consulta justo antes de que se complete la consulta. Los datos aquí se agregan en todos los hilos.

ingrese la descripción de la imagen aquí

Si excavo en cada subproceso individualmente, veo que 2 subprocesos completan la clasificación en aproximadamente 5 segundos (@ 20 segundos en total, después de 15 segundos dedicados al escaneo de la tabla). Si todos los hilos progresaran a este ritmo, la VARCHAR(256)creación del índice se habría completado aproximadamente al mismo tiempo que la tabla original.

Sin embargo, los 6 hilos restantes progresan a un ritmo mucho más lento. Esto puede deberse a la forma en que se asigna la memoria y la forma en que los subprocesos están siendo retenidos por E / S a medida que se están derramando datos. Sin embargo, no estoy seguro.

ingrese la descripción de la imagen aquí


¿Qué puedes hacer?

Hay varias cosas que podrías considerar probar:

  • Trabaje con el proveedor para volver a una versión anterior. Si eso no es posible, informe al proveedor que no está satisfecho con este cambio para que pueda considerar revertirlo en una versión futura.
  • Cuando agregue su índice, considere usar OPTION (MAXDOP X)where Xes un número menor que su configuración actual de nivel de servidor. Cuando utilicé OPTION (MAXDOP 2)este conjunto de datos específico en mi máquina, la VARCHAR(256)versión se completó en 25 seconds(¡ en comparación con 3-4 minutos con 8 hilos!). Es posible que el comportamiento de derrame se vea exacerbado por un mayor paralelismo.
  • Si existe la posibilidad de invertir en hardware adicional, perfile la E / S (el posible cuello de botella) en su sistema y considere usar un SSD para reducir la latencia de la E / S incurrida por los derrames.


Otras lecturas

Paul White tiene una buena publicación de blog sobre los aspectos internos de SQL Server que pueden ser de interés. Habla un poco sobre derrames, sesgos de hilos y asignación de memoria para géneros paralelos.

Geoff Patterson
fuente
11

La tabla de clasificación intermedia se estimará de manera diferente entre los dos casos. Esto conducirá a diferentes solicitudes de concesión de memoria ( VARCHAR(256)será mayor) y probablemente a una concesión real mucho menor, en porcentaje, en comparación con la solicitud 'ideal'. Supongo que esto lleva a derrames durante la clasificación.

Al probar el script de Geoff (solo en 100k filas), puedo ver claramente la diferencia en el tamaño estimado de la fila (141B vs. 789B). A partir de esto, las cosas caen en cascada.

Remus Rusanu
fuente
8
Estoy seguro de que Paul demostrará una respuesta más completa y completa, incluidas las pilas de llamadas, que el equipo de desarrollo utilizará como material de aprendizaje. De nuevo ...
Remus Rusanu