¿Debo deshabilitar las "estadísticas de actualización automática" en un escenario de almacenamiento de datos?

12

Tengo un almacén de datos de 200 GB en SQL Server.

He estado experimentando tiempos de ejecución realmente lentos para algunas consultas; por ejemplo 12 horas para una deleteconsulta simple con un inner join.

Después de investigar un poco con los planes de ejecución, actualicé las estadísticas de las 2 tablas involucradas en la consulta, utilizando la WITH FULLSCANopción.

La consulta ahora se ejecuta en menos de un segundo, por lo que parece que las estadísticas no estaban actualizadas.

Estoy considerando deshabilitar auto update statisticsla base de datos y ejecutarla UPDATE STATISTICSmanualmente después de cargar el almacén de datos. El almacén de datos se carga de forma incremental desde un sistema ERP de origen diariamente, por la noche.

¿Estoy en lo cierto al suponer que auto update statisticsen un escenario de almacenamiento de datos no es realmente útil? En cambio, ¿tiene más sentido actualizar las estadísticas manualmente después de cargar los datos?

saso
fuente
Esta es una muy buena lectura sobre estadísticas: simple-talk.com/sql/performance/… también ejecutamos un trabajo diario utilizando la solución ola.hallengren.com/… de Ola para actualizar estadísticas en 1TB db. No deshabilitaría la opción de estadísticas de actualización automática.
Joy Walker
1
Depende mucho de cuántos registros haya en su (s) tabla (s) y cuánto agregue en un lote. En una tabla de filas 1b donde agrega 20 millones de filas por noche, sus estadísticas se actualizarán cada 10 días, lo que no es bueno.
JNK
2
Solo para su información: hay un indicador de seguimiento (2371) para cambiar el umbral para la actualización de estadísticas del 20% fijo a una tasa de porcentaje dinámico. Ver más aquí: blogs.msdn.com/b/saponsqlserver/archive/2011/09/07/…
DaniSQL
Enlaces muy informativos, gracias! @JNK: Sí, es un DB grande. La tabla de etapas tiene más de 300m de filas y, según el día, estamos insertando algo entre 1m y 10m diariamente. Echaremos un vistazo más de cerca a las estadísticas, como lo propuso la respuesta a continuación.
saso

Respuestas:

11

Aquí hay un documento técnico sobre cuándo se produce la actualización automática de estadísticas . Estos son los puntos más destacados con respecto a las actualizaciones automáticas de estadísticas:

  • El tamaño de la tabla ha pasado de 0 a> 0 filas (prueba 1).
  • El número de filas en la tabla cuando se recopilaron las estadísticas era de 500 o menos, y el colmodctr de la columna principal del objeto de estadísticas ha cambiado en más de 500 desde entonces (prueba 2).
  • La tabla tenía más de 500 filas cuando se recopilaron las estadísticas, y el colmodctr de la columna inicial del objeto de estadísticas ha cambiado en más de 500 + 20% del número de filas en la tabla cuando se recopilaron las estadísticas (prueba 3) .

Entonces, @JNK señaló en un comentario que si tiene mil millones de filas en una tabla, necesitaría tener 20,000,5000 escrituras en la primera columna de la estadística para activar una actualización.

Tomemos la siguiente estructura:

CREATE TABLE dbo.test_table (
    test_table_id INTEGER IDENTITY(1,1) NOT NULL, 
    test_table_value VARCHAR(50), 
    test_table_value2 BIGINT, 
    test_table_value3 NUMERIC(10,2)
);

CREATE CLUSTERED INDEX cix_test_table ON dbo.test_table (test_table_id, test_table_value);

Ahora podemos verificar qué sucedió en las estadísticas de la tierra.

select * 
    from sys.stats
        where object_id = OBJECT_ID('dbo.test_table')

stat_container

Sin embargo, para ver si este es un objeto estadístico significativo, necesitamos:

dbcc show_statistics('dbo.test_table',cix_test_table)

histograma

Entonces esta estadística no se ha actualizado. Esto se debe a que parece que la estadística no se actualiza hasta que SELECTocurre un e incluso entonces SELECTtiene que quedar fuera de lo que SQL Server tiene dentro de su histograma. Aquí hay un script de prueba que ejecuté para probar esto:

    CREATE TABLE test_table (
        test_table_id INTEGER IDENTITY(1,1) NOT NULL, 
        test_table_value VARCHAR(50), 
        test_table_value2 BIGINT, 
        test_table_value3 NUMERIC(10,2)
    );

    CREATE CLUSTERED INDEX cix_test_table ON test_table (test_table_id, test_table_value);

    ALTER TABLE test_table ADD CONSTRAINT pk_test_table PRIMARY KEY  (test_table_id)

    SELECT * 
        FROM sys.stats
            WHERE object_id = OBJECT_ID('dbo.test_table')

    --DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table)
    DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;

declare @test int = 0

WHILE @test < 1
    BEGIN
        INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
            ('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
        SET @test = @test + 1;
    END

SELECT 'one row|select < 1', * FROM test_table WHERE test_table_id < 1;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;

SET @test = 1

WHILE @test < 500
    BEGIN
        INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
            ('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
        SET @test = @test + 1;
    END

SELECT '100 rows(add 99)|select < 100',* FROM test_table WHERE test_table_id < 100;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;
--get the table up to 500 rows/changes
WHILE @test < 500
    BEGIN
        INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
            ('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
        SET @test = @test + 1;
    END
SELECT '500 rows(add 400)|select < 100',* FROM test_table WHERE test_table_id < 100;
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;
SELECT '500 rows(add 400)|select < 500',* FROM test_table WHERE test_table_id < 500;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;
--bump it to 501
SET @test = 500;
WHILE @test < 501
    BEGIN
        INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
            ('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
        SET @test = @test + 1;
    END


SELECT '501 rows(add 1)|select < 501',* FROM test_table WHERE test_table_id < 501;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;

--bump it to 600
SET @test = 501;
WHILE @test < 600
    BEGIN
        INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
            ('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
        SET @test = @test + 1;
    END

SELECT '600 rows (add 100)|select < 600',* FROM test_table WHERE test_table_id < 600;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;

--bump it to 700
SET @test = 600;
WHILE @test < 700
    BEGIN
        INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
            ('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
        SET @test = @test + 1;
    END

SELECT '700 rows (add 100)|select < 700', * FROM test_table WHERE test_table_id < 700;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;

--bump it to 1200
SET @test = 700;
WHILE @test < 1200
    BEGIN
        INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
            ('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
        SET @test = @test + 1;
    END

SELECT '1200 rows (add 500)|select < 1200',* FROM test_table WHERE test_table_id < 1200;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;
--DROP TABLE test_table

En lugar de deshabilitar ciegamente las estadísticas de auto_update, trataría de examinar su conjunto de datos en busca de sesgo. Si sus datos muestran un sesgo significativo, entonces debe considerar la creación de estadísticas filtradas y luego decidir si administrar las actualizaciones de estadísticas manualmente es el curso de acción correcto.

Para analizar la asimetría, debe ejecutar DBCC SHOW_STATISTICS(<stat_object>, <index_name>);(en el script anterior sin el WITH STAT_HEADER) la combinación estadística / índice particular que desea examinar. Una forma rápida de mirar su sesgo sería mirar el histograma (tercer conjunto de resultados) y verificar la varianza en su EQ_ROWS. Si es bastante consistente, entonces tu sesgo es mínimo. Para avanzar, mire la RANGE_ROWScolumna y observe la varianza allí, ya que esto mide cuántas filas existen entre cada paso. Finalmente, puede tomar el [All density]resultado del DENSITY_VECTOR(segundo conjunto de resultados) y multiplicarlo por el [Rows Sampled]valor en el STAT_HEADER(primer conjunto de resultados) y ver cuál sería la expectativa promedio para una consulta en esa columna. Compara ese promedio con suEQ_ROWS y si hay muchos lugares donde varía significativamente, entonces tienes sesgo.

Si encuentra que tiene sesgo, entonces debe considerar la creación de algunas estadísticas filtradas en los rangos que tienen alto muy alto RANGE_ROWSpara que pueda dar pasos adicionales para mejores estimaciones de esos valores.

Una vez que tenga estas estadísticas filtradas, puede ver la posibilidad de actualizar las estadísticas manualmente.

swasheck
fuente
Gracias por la respuesta integral. No tengo mucha experiencia con las estadísticas y esto parece ser más complicado como pensaba. Definitivamente echaremos un vistazo más de cerca y seguiremos sus pautas.
saso