Dois-je désactiver les «statistiques de mise à jour automatique» dans un scénario d'entreposage de données?


12

J'ai un entrepôt de données de 200 Go dans SQL Server.

J'ai connu des temps d'exécution très lents pour certaines requêtes; par exemple 12 heures pour une simple deleterequête avec un inner join.

Après avoir fait quelques recherches avec les plans d'exécution, j'ai mis à jour les statistiques des 2 tables impliquées dans la requête, en utilisant l' WITH FULLSCANoption.

La requête s'exécute maintenant en moins d'une seconde, il semble donc que les statistiques n'étaient pas à jour.

J'envisage de désactiver auto update statisticsla base de données et de l'exécuter UPDATE STATISTICSmanuellement après le chargement de l'entrepôt de données. L'entrepôt de données est chargé de manière incrémentielle à partir d'un système ERP source quotidiennement, la nuit.

Ai-je raison de supposer que auto update statisticsdans un scénario d'entreposage de données, ce n'est pas vraiment utile? Au lieu de cela, est-il plus logique de mettre à jour les statistiques manuellement après le chargement des données?


C'est une très bonne lecture sur les statistiques: simple-talk.com/sql/performance/… nous exécutons également un travail quotidien en utilisant la solution Ola ola.hallengren.com/… pour mettre à jour les statistiques sur 1 To de base de données. Je ne désactiverais pas l'option de mise à jour automatique des statistiques.
Joy Walker

1
Cela dépend beaucoup du nombre d'enregistrements dans vos tables et de la quantité que vous ajoutez dans un lot. Dans un tableau de 1b de lignes où vous ajoutez 20m de lignes par nuit, vos statistiques seront mises à jour tous les 10 jours, ce qui n'est pas génial.
JNK

2
Juste pour info - il existe un indicateur de trace (2371) pour changer le seuil de mise à jour des statistiques de 20% fixe à un taux de pourcentage dynamique. Voir plus ici: blogs.msdn.com/b/saponsqlserver/archive/2011/09/07/…
DaniSQL

Liens très informatifs, merci! @JNK: Oui, c'est une grande base de données. La table de mise en scène a 300m + rangées et selon le jour, nous insérons quelque chose entre 1m et 10m par jour. Examinera de plus près les statistiques, comme cela a été proposé par la réponse ci-dessous.
saso

Réponses:


11

Voici un livre blanc sur le moment où la mise à jour automatique des statistiques se produit . Voici les points saillants des mises à jour automatiques des statistiques:

  • La taille du tableau est passée de 0 à> 0 lignes (test 1).
  • Le nombre de lignes dans le tableau lors de la collecte des statistiques était de 500 ou moins, et le colmodctr de la première colonne de l'objet de statistiques a changé de plus de 500 depuis lors (test 2).
  • Le tableau comportait plus de 500 lignes lors de la collecte des statistiques et le colmodctr de la colonne de tête de l'objet statistique a changé de plus de 500 + 20% du nombre de lignes du tableau lors de la collecte des statistiques (test 3) .

@JNK a donc fait remarquer dans un commentaire que si vous avez 1 milliard de lignes dans une table, vous auriez besoin d'avoir 20 000 500 000 écritures dans la première colonne de la statistique pour déclencher une mise à jour.

Prenons la structure suivante:

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);

Maintenant, nous pouvons vérifier pour voir ce qui s'est passé dans les statistiques.

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

stat_container

Cependant, pour voir s'il s'agit d'un objet statistique significatif, nous devons:

dbcc show_statistics('dbo.test_table',cix_test_table)

histogramme

Cette statistique n'a donc pas été mise à jour. En effet, il semble que la statistique ne soit pas mise à jour jusqu'à ce qu'un SELECTse produise et même alors, le SELECTdoit être en dehors de ce que SQL Server a dans son histogramme. Voici un script de test que j'ai exécuté pour tester ceci:

    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

Au lieu de désactiver aveuglément les statistiques auto_update, j'essayerais d'examiner votre ensemble de données pour le biais. Si vos données présentent un biais important, vous devez envisager de créer des statistiques filtrées, puis décider si la gestion manuelle des mises à jour des statistiques est la bonne solution.

Pour analyser le biais, vous devez exécuter DBCC SHOW_STATISTICS(<stat_object>, <index_name>);(dans le script ci-dessus sans le WITH STAT_HEADER) sur la combinaison stat / index particulière que vous souhaitez examiner. Un moyen rapide de surveiller votre asymétrie serait de regarder l'histogramme (troisième jeu de résultats) et de vérifier la variance de votre EQ_ROWS. Si c'est assez cohérent, votre asymétrie est minime. Pour l'intensifier, vous regardez la RANGE_ROWScolonne et regardez la variance car elle mesure le nombre de lignes entre chaque étape. Enfin, vous pouvez prendre le [All density]résultat du DENSITY_VECTOR(deuxième jeu de résultats) et le multiplier par la [Rows Sampled]valeur du STAT_HEADER(premier jeu de résultats) et voir quelle serait l'attente moyenne pour une requête sur cette colonne. Vous comparez cette moyenne à votreEQ_ROWS et s'il y a beaucoup d'endroits où cela varie considérablement, alors vous avez un biais.

Si vous constatez que vous avez une asymétrie, vous devez envisager de créer des statistiques filtrées sur les plages qui sont élevées très élevées RANGE_ROWSafin de pouvoir donner des étapes supplémentaires pour de meilleures estimations sur ces valeurs.

Une fois ces statistiques filtrées en place, vous pouvez envisager la possibilité de mettre à jour manuellement les statistiques.


Merci pour la réponse complète. Je n'ai pas beaucoup d'expérience avec les statistiques et cela semble être plus délicat comme je le pensais. Je vais certainement y regarder de plus près et suivre vos directives.
saso
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.