Следует ли отключить «автоматическое обновление статистики» в сценарии с хранилищем данных?

12

У меня есть хранилище данных 200 ГБ в SQL Server.

Я испытываю очень медленное время выполнения некоторых запросов; например 12 часов для простого deleteзапроса с inner join.

Проведя некоторые исследования с планами выполнения, я обновил статистику двух таблиц, задействованных в запросе, используя эту WITH FULLSCANопцию.

Теперь запрос выполняется менее чем за секунду, поэтому кажется, что статистика не была обновлена.

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

Правильно ли я предположить, что auto update statisticsв сценариях хранилищ данных не очень полезно? Вместо этого, имеет ли смысл обновлять статистику вручную после загрузки данных?

SASO
источник
Это очень хорошее прочтение о статистике: simple-talk.com/sql/performance/… мы также выполняем ежедневную работу, используя решение Ola ola.hallengren.com/… для обновления статистики на 1 ТБ дБ. Я бы не стал отключать опцию автоматического обновления статистики.
Джой Уокер
1
Это во многом зависит от того, сколько записей в ваших таблицах и сколько вы добавляете в пакет. В таблице 1b строк, где вы добавляете 20 миллионов строк в ночь, ваша статистика будет обновляться каждые 10 дней, что не очень хорошо.
JNK
2
Просто к вашему сведению - есть флаг трассировки (2371) для изменения порога обновления статистики с фиксированного 20% на динамический процент. Смотрите больше здесь: blogs.msdn.com/b/saponsqlserver/archive/2011/09/07/…
DaniSQL
Очень информативные ссылки, спасибо! @JNK: Да, это большая БД. Промежуточный стол содержит более 300 миллионов строк, и в зависимости от дня мы вставляем что-то от 1 до 10 миллионов в день. Более подробно рассмотрим статистику, как это было предложено в ответе ниже.
Сасо

Ответы:

11

Вот технический документ, когда происходит автоматическое обновление статистики . Вот основные моменты в отношении автоматических обновлений статистики:

  • Размер таблицы изменился с 0 до> 0 строк (тест 1).
  • Число строк в таблице, когда собирались статистические данные, составляло 500 или менее, и с тех пор colmodctr ведущего столбца объекта статистики изменилось более чем на 500 (тест 2).
  • На момент сбора статистики в таблице было более 500 строк, а colmodctr в первом столбце объекта статистики изменилось более чем на 500 + 20% от числа строк в таблице, когда была собрана статистика (тест 3) ,

Таким образом, @JNK указал в комментарии, что если у вас есть 1 миллиард строк в таблице, вам потребуется 20 000 5 000 записей в первый столбец статистики, чтобы запустить обновление.

Давайте возьмем следующую структуру:

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

Вместо того, чтобы слепо отключать статистику auto_update, я бы попытался проверить ваш набор данных на перекос. Если ваши данные демонстрируют значительный перекос, то вам нужно подумать о создании отфильтрованной статистики, а затем решить, является ли управление обновлениями статистики вручную правильным способом.

Чтобы проанализировать асимметрию, вам нужно запустить 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чтобы вы могли дать дополнительные шаги для более точных оценок этих значений.

После того, как у вас есть эти отфильтрованные статистические данные, вы можете посмотреть на возможность ручного обновления статистики.

swasheck
источник
Спасибо за исчерпывающий ответ. У меня не так много опыта со статистикой, и, как мне показалось, все сложнее. Обязательно присмотритесь и следуйте вашим указаниям.
Сасо