Обычно, когда SQL Server обновляет статистику по объекту, он также аннулирует кешированные планы выполнения, которые полагаются на эту статистику. Именно поэтому вы видите повторную компиляцию после обновления статистики: SQL Server знает, что статистика изменилась, и это хороший момент для построения новых планов выполнения с учётом изменений в данных.
Однако обновление статистики, созданной системой, не обязательно приводит к повторной компиляции планов.
Это действительно странный крайний случай, и вы, вероятно, никогда с ним не столкнётесь, но я сталкиваюсь с ним на каждом занятии, которое провожу. Я каждый раз мимоходом упоминаю об этом в классе и даже не обращаю на это особого внимания. Однако недавно студент спросил меня: «Это где-нибудь задокументировано?», и я подумал, э-э, может быть, но я не уверен, так что лучше задокументировать это здесь, в старом добром блоге.
Демонстрация проблемы
Чтобы проиллюстрировать это, я возьму любую версию базы данных Stack Overflow (я использую большую версию 2024 года), удалю индексы, очищу кеш планов. Я использую уровень совместимости 170 (2025), потому что демонстрирую это на SQL Server 2025, и хочу доказать, что это всё ещё не исправлено в 2025. Затем запущу запрос к таблице Users:
-- Удаляем индексы (предполагается, что процедура DropIndexes существует)
EXEC DropIndexes;
GO
-- Очищаем кеш планов выполнения
DBCC FREEPROCCACHE;
GO
-- Устанавливаем уровень совместимости базы данных
ALTER DATABASE CURRENT SET COMPATIBILITY_LEVEL = 170; /* 2025 */
GO
-- Выполняем тестовый запрос
SELECT TOP 101 *
FROM dbo.Users
WHERE Location = N'Netherlands'
ORDER BY Reputation DESC;
GO
Чтобы выполнить этот запрос, SQL Server должен предположить, сколько строк соответствуют нашему предикату Location = 'Netherlands', поэтому он автоматически создаёт статистику по столбцу Location «на лету». Давайте проверим это с помощью sp_BlitzIndex, которая возвращает строку результирующего набора со всеми данными гистограммы статистики для этой таблицы:
Я прокручу вниз до области «Netherlands» и покажу ещё несколько релевантных столбцов. Вам захочется нажать на это изображение, чтобы увеличить, если вы хотите следить за моим объяснением ниже — то есть, если вы не доверяете моему письменному объяснению, чему, конечно же, нет причин, потому что вы высоко цените мою репутацию, я уверен:
Что следует отметить на этом снимке экрана:
- Это автоматически созданная статистика с именем, начинающимся с
_WA_Sys(создана системой). - Она была выборочной: обратите внимание на дробные числа для range rows и equal rows, а также на «Rows Sampled» справа.
- Она была последний раз обновлена
2025-12-24 01:10:55.0733333— это говорит вам, что я пишу этот пост в канун Рождества, но время странное, потому что я пишу это из отеля в Китае, а мой сервер в UTC, так что одному Богу известно, который час у вас, и нет, вам не нужно беспокоиться о моём психическом здоровье, даже несмотря на то, что я пишу в блоге в канун Рождества, потому что я пишу это за завтраком в отеле, пока жду, когда Ив проснётся и соберётся, потому что мы идём сегодня в Шанхайский Диснейленд, в котором лучшие сорта попкорна из всех парков Диснейленда в мире, и вам придётся поверить мне на слово, но они потрясающие, серьёзно, кто бы мог подумать, что лимонный попкорн так хорош на вкус? - Обратите внимание на оценку для Netherlands: Equal Rows —
17029.47.
Обновляем статистику и проверяем
Теперь предположим, что у нас возникли проблемы с производительностью, и мы решили обновить статистику с полным сканированием (FULLSCAN). Затем мы снова проверим статистику:
-- Обновляем статистику таблицы с полным сканированием
UPDATE STATISTICS dbo.Users WITH FULLSCAN;
GO
/* Проверяем статистику по столбцу Location снова: */
EXEC sp_BlitzIndex @TableName = 'Users';
GO
Обновлённая статистика для Netherlands действительно изменилась:
Что следует отметить на этом снимке экрана после того, как вы нажмёте на него, громко сказав слово «УВЕЛИЧИТЬ!»:
- Значение Netherlands Equal Rows изменилось на
17100. - Теперь все числа целые, потому что Rows Sampled совпадает с размером таблицы.
- Дата последнего обновления статистики изменилась на
2025-12-24 01:17:54.5133333, то есть прошло около 6 минут, что даёт вам представление о том, каково писать пост в блоге — всё это выглядит обманчиво легко, но это не так, и я уже легко потратил на это час: написал демонстрацию, столкнулся с несколькими препятствиями по пути, затем начал писать пост и делать снимки экрана, но на самом деле неважно, сколько времени это занимает, потому что я уверен, что Ив будет готов «через пять минут», и мы оба знаем, что это значит, и под «мы оба» я имею в виду вас и меня, дорогой читатель.
Запускаем запрос снова
Итак, если я снова запущу тот же запрос, он получит новый план, верно? Давайте посмотрим на его фактический план выполнения:
Сосредоточьтесь на числах в правом нижнем углу: SQL Server вернул 17 100 строк при оценке в 17 030 строк. Эта оценка в 17 030 говорит вам о том, что мы не получили новый план запроса — оценки взяты из первоначального выборочного запуска статистики. Другой способ убедиться в этом — проверить кеш планов с помощью sp_BlitzCache:
Я переставил столбцы для удобства снимка экрана — обычно эти два столбца находятся дальше справа:
- # Executions — один и тот же план запроса был выполнен 2 раза.
- PlanGenerationNum — 1, потому что это всё ещё первая вариация этого плана запроса.
Вывод и практическая значимость
Итак, мы видим, что если изменяется статистика, созданная системой, даже если её содержимое изменилось, это всё равно не вызывает автоматической перекомпиляции связанных планов. Если вам нужны новые планы для этих объектов, вам нужно будет сделать что-то вроде sp_recompile с передачей имени таблицы.
В реальном мире нужно ли об этом беспокоиться? Вероятно, нет, потому что в реальном мире вы, вероятно, больше обеспокоены статистикой по индексам, и ваш кеш планов, скорее всего, и так очень нестабилен. Кроме того, в большинстве случаев я предпочёл бы ошибиться в сторону стабильности кеша планов, а не его постоянной смены.
Теперь у некоторых из вас возникнут дополнительные вопросы, или вы захотите воспроизвести эту демонстрацию на своих собственных машинах, со своей версией SQL Server, со своей базой данных Stack Overflow (или своими таблицами). Вы неизбежно столкнётесь с множеством разных подводных камней в подобных демонстрациях, потому что статистика и планы запросов — это сложные темы. Например, если вы получили полное сканирование при первоначальном создании статистики (потому что у вас был очень маленький объект — без осуждения — или потому что у вас был старый уровень совместимости), то вы можете даже не ожидать увидеть изменения в статистике. Я не буду помогать вам в устранении неполадок с воспроизведением демонстрации именно в этом сообщении в блоге просто потому, что это потребовало бы много индивидуальной помощи, но если у вас возникнут вопросы, вы можете оставить комментарий, и, возможно, другой читатель будет готов потратить своё время, чтобы помочь вам.






Комментариев нет:
Отправить комментарий