¿Por qué la segunda INSERT
declaración es ~ 5 veces más lenta que la primera?
Por la cantidad de datos de registro generados, creo que el segundo no califica para un registro mínimo. Sin embargo, la documentación en la Guía de rendimiento de carga de datos indica que ambas inserciones deben poder registrarse mínimamente. Entonces, si el registro mínimo es la diferencia clave de rendimiento, ¿por qué la segunda consulta no califica para un registro mínimo? ¿Qué se puede hacer para mejorar la situación?
Consulta # 1: Insertar filas de 5MM usando INSERT ... WITH (TABLOCK)
Considere la siguiente consulta, que inserta filas de 5MM en un montón. Esta consulta se ejecuta 1 second
y genera 64MB
datos de registro de transacciones según lo informado por sys.dm_tran_database_transactions
.
CREATE TABLE dbo.minimalLoggingTest (n INT NOT NULL)
GO
INSERT INTO dbo.minimalLoggingTest WITH (TABLOCK) (n)
SELECT n
-- Any table/view/sub-query that correctly estimates that it will generate 5MM rows
FROM dbo.fiveMillionNumbers
-- Provides greater consistency on my laptop, where other processes are running
OPTION (MAXDOP 1)
GO
Consulta # 2: Insertar los mismos datos, pero SQL subestima el número de filas
Ahora considere esta consulta muy similar, que opera exactamente con los mismos datos pero que se basa en una tabla (o SELECT
declaración compleja con muchas combinaciones en mi caso de producción real) donde la estimación de cardinalidad es demasiado baja. Esta consulta se ejecuta 5.5 seconds
y genera 461MB
datos de registro de transacciones.
CREATE TABLE dbo.minimalLoggingTest (n INT NOT NULL)
GO
INSERT INTO dbo.minimalLoggingTest WITH (TABLOCK) (n)
SELECT n
-- Any table/view/sub-query that produces 5MM rows but SQL estimates just 1000 rows
FROM dbo.fiveMillionNumbersBadEstimate
-- Provides greater consistency on my laptop, where other processes are running
OPTION (MAXDOP 1)
GO
Guión completo
Consulte este Pastebin para obtener un conjunto completo de scripts para generar los datos de prueba y ejecutar cualquiera de estos escenarios. Tenga en cuenta que debe usar una base de datos que esté en el SIMPLE
modelo de recuperación .
Contexto empresarial
Con frecuencia, nos movemos alrededor de millones de filas de datos, y es importante que estas operaciones sean lo más eficientes posible, tanto en términos del tiempo de ejecución como de la carga de E / S del disco. Inicialmente teníamos la impresión de que crear una tabla de almacenamiento dinámico y usarlo INSERT...WITH (TABLOCK)
era una buena manera de hacerlo, pero ahora nos hemos vuelto menos seguros dado que observamos la situación demostrada anteriormente en un escenario de producción real (aunque con consultas más complejas, no el versión simplificada aquí).
SELECT
declaración compleja con numerosas combinaciones que genera el conjunto de resultados paraINSERT
. Estas uniones producen estimaciones de cardinalidad deficientes para el operador de inserción de la tabla final (que he simulado en el script de repro a través de laUPDATE STATISTICS
llamada incorrecta ) y, por lo tanto, no es tan simple como emitir unUPDATE STATISTICS
comando para solucionar el problema. Estoy totalmente de acuerdo en que simplificar la consulta para que sea más fácil de entender para el Estimador de cardinalidad podría ser un buen enfoque, pero no es una buena opción implementar una lógica comercial compleja.