Чи слід відключити "статистику автоматичного оновлення" в сценарії зберігання даних?


12

У мене є сховище даних на 200 ГБ в SQL Server.

Я переживаю дуже повільні терміни виконання деяких запитів; наприклад, 12 годин на простий deleteзапит із запитом inner join.

Провівши кілька досліджень із планами виконання, я оновив статистику двох таблиць, що беруть участь у запиті, використовуючи WITH FULLSCANпараметр.

Зараз запит виконується менше ніж за секунду, тому, схоже, статистика не була актуальною.

Я розглядаю можливість відключення auto update statisticsбази даних та запуску UPDATE STATISTICSвручну після завантаження сховища даних. Склад даних завантажується поступово з джерельної ERP-системи щодня, вночі.

Чи правильно я вважаю, що auto update statisticsсценарії зберігання даних не дуже корисні? Натомість, чи має сенс оновлювати статистику вручну після завантаження даних?


Це дуже вдале прочитання статистики: simple-talk.com/sql/performance/… ми також виконуємо щоденну роботу, використовуючи рішення Ola ola.hallengren.com/…, щоб оновити статистику на 1 ТБ дБ. Я б не відключив опцію статистики автоматичного оновлення.
Радість Уокер

1
Це багато що залежить від того, скільки записів у ваших таблицях (ях) і скільки ви додаєте в партію. У таблиці 1b рядків, де ви додаєте 20м рядків за ніч, ваша статистика оновлюватиметься кожні 10 днів, що не чудово.
JNK

2
Просто FYI - є прапор сліду (2371), щоб змінити поріг оновлення статистики з фіксованих 20% до динамічної процентної ставки. Більше тут: blogs.msdn.com/b/saponsqlserver/archive/2011/09/07/…
DaniSQL

Дуже інформативні посилання, дякую! @JNK: Так, це велика БД. Постановочний стіл містить 300м + рядків, і залежно від дня ми вставляємо щоденно від 1м до 10м. Детальніше розглянемо статистику, як це було запропоновано у відповіді нижче.
сасо

Відповіді:


11

Ось довідка про те, коли відбувається автоматичне оновлення статистики . Ось важливі моменти щодо автоматичних оновлень статистики:

  • Розмір таблиці перемістився від 0 до> 0 рядків (тест 1).
  • Кількість рядків у таблиці, коли збиралася статистика, становила 500 або менше, а комодктрик провідного стовпця об’єкта статистики з цього часу змінився більш ніж на 500 (тест 2).
  • Коли зібрана статистика, таблиця мала більше 500 рядків, а комодктр провідного стовпця об’єкта статистики змінився більш ніж на 500 + 20% від кількості рядків у таблиці під час збирання статистики (тест 3) .

Тож @JNK зазначив у коментарі, що якщо у вас є 1 мільярд рядків у таблиці, вам потрібно буде мати 20 000,5000 записів у перший стовпець статистики, щоб запустити оновлення.

Візьмемо таку структуру:

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

Тепер ми можемо перевірити, що сталося в статистиці земельних ділянок.

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

stat_container

Однак, щоб зрозуміти, чи є це значимим статистичним об'єктом, нам потрібно:

dbcc show_statistics('dbo.test_table',cix_test_table)

гістограма

Тож ця статистика не оновлювалася. Це тому, що схоже, що статистика не оновлюється, поки не SELECTвідбудеться, і навіть тоді SELECTвона повинна виходити за межі того, що має SQL Server у своїй гістограмі. Ось тестовий сценарій, який я запустив, щоб перевірити це:

    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

Замість того, щоб сліпо вимкнути статистику автоматичного оновлення, я б спробував вивчити ваш набір даних на предмет перекосу. Якщо дані показує значний перекіс, то вам необхідно розглянути питання про створення відфільтрованих статистиків і потім вирішити , якщо управління оновленням статистики вручну правильний курс дій.

Щоб проаналізувати перекос, потрібно запустити DBCC SHOW_STATISTICS(<stat_object>, <index_name>);(у наведеному вище сценарії без WITH STAT_HEADER) відповідну комбінацію stat / index, яку ви хочете вивчити. Швидкий спосіб зав'язати очне яблуко - переглядати гістограму (третій набір результатів) і перевірити дисперсію вашої EQ_ROWS. Якщо це досить послідовно, то ваш перекос мінімальний. Щоб активізувати це, ви дивитесь на RANGE_ROWSстовпчик і дивитесь на дисперсію там, оскільки це вимірює кількість рядків між кожним кроком. Нарешті, ви можете взяти [All density]результат із DENSITY_VECTOR(другий набір результатів) і помножити його на [Rows Sampled]значення в STAT_HEADER(перший набір результатів) і побачити, яким буде середнє очікування для запиту в цьому стовпчику. Ви порівнюєте це середнє значення з вашимEQ_ROWS і якщо є багато місць, де вона значно змінюється, то ви перекосилися.

Якщо ви виявите, що у вас є перекос, вам потрібно розглянути можливість створення декількох відфільтрованих статистичних даних про діапазони, які мають високий і дуже високий рівень, RANGE_ROWSщоб ви могли дати додаткові кроки для кращих оцінок цих значень.

Коли у вас є відфільтрована статистика, ви зможете переглянути можливість вручну оновити статистику.


Дякую за вичерпну відповідь. Я не маю такого великого досвіду роботи зі статистикою, і це здається більш складним, як я думав. Ви точно ознайомтесь детальніше та дотримуйтесь ваших вказівок.
сасо
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.