Remus ha señalado útilmente que la longitud máxima de la VARCHAR
columna 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 256
la 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 LOG
archivo. 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 :)
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?
Plan de consulta real: VARCHAR (256)
Desde el plan de consulta, podemos ver que el Sort
operador 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).
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 Scan
y Sort
se procesan sin derrames ( spill_page_count
permanece en 0
todo momento).
En la VARCHAR(256)
versión, sin embargo, podemos ver que los derrames de páginas se acumulan rápidamente para el Sort
operador. 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.
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.
¿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 X
es 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.