Скажу сразу , что мой вопрос / проблема выглядит похожа на эту предыдущий, но так как я не уверен , если причина или начальная информация является таким же, я решил разместить свой вопрос еще с некоторыми деталями.
Проблема под рукой:
- в неурочный час (ближе к концу рабочего дня) производственный экземпляр начинает работать беспорядочно:
- например, высокая загрузка ЦП (по сравнению с 30% она выросла примерно вдвое и продолжала расти)
- увеличено количество транзакций в секунду (хотя загрузка приложения не изменилась)
- увеличено количество бездействующих сеансов
- странные события блокировки между сессиями, которые никогда не отображали это поведение (даже чтение незафиксированных сессий вызывало блокировку)
- лучшие ожидания интервала были защелкой страницы на 1-м месте с блокировками на 2-м месте
Начальное расследование:
- Используя sp_whoIsActive, мы увидели, что запрос, выполняемый нашим инструментом мониторинга, решает работать очень медленно и захватывает много ЦП, чего раньше не было;
- его уровень изоляции считывался незафиксированным;
- мы посмотрели на план, мы увидели дурацкие цифры: StatementEstRows = "3.86846e + 010" с примерно 150 ТБ предполагаемых данных, которые должны быть возвращены
- мы подозревали, что причиной была функция мониторинга запросов инструмента мониторинга, поэтому мы отключили эту функцию (мы также открыли тикет у нашего провайдера, чтобы проверить, знают ли они о какой-либо проблеме)
- с того первого события это происходило еще несколько раз, и каждый раз, когда мы убиваем сессию, все возвращается на круги своя;
- мы понимаем, что запрос чрезвычайно похож на один из запросов, используемых MS в BOL для мониторинга хранилища запросов - запросы, которые недавно снизились в производительности (сравнивая различные моменты времени)
- мы выполняем один и тот же запрос вручную и видим одно и то же поведение (использование процессора увеличивается, увеличивается время ожидания защелки, неожиданные блокировки и т. д.)
Виновный запрос:
Select qt.query_sql_text,
q.query_id,
qt.query_text_id,
rs1.runtime_stats_id AS runtime_stats_id_1,
interval_1 = DateAdd(minute, -(DateDiff(minute, getdate(), getutcdate())), rsi1.start_time),
p1.plan_id AS plan_1,
rs1.avg_duration AS avg_duration_1,
rs2.avg_duration AS avg_duration_2,
p2.plan_id AS plan_2,
interval_2 = DateAdd(minute, -(DateDiff(minute, getdate(), getutcdate())), rsi2.start_time),
rs2.runtime_stats_id AS runtime_stats_id_2
From sys.query_store_query_text AS qt
Inner Join sys.query_store_query AS q
ON qt.query_text_id = q.query_text_id
Inner Join sys.query_store_plan AS p1
ON q.query_id = p1.query_id
Inner Join sys.query_store_runtime_stats AS rs1
ON p1.plan_id = rs1.plan_id
Inner Join sys.query_store_runtime_stats_interval AS rsi1
ON rsi1.runtime_stats_interval_id = rs1.runtime_stats_interval_id
Inner Join sys.query_store_plan AS p2
ON q.query_id = p2.query_id
Inner Join sys.query_store_runtime_stats AS rs2
ON p2.plan_id = rs2.plan_id
Inner Join sys.query_store_runtime_stats_interval AS rsi2
ON rsi2.runtime_stats_interval_id = rs2.runtime_stats_interval_id
Where rsi1.start_time > DATEADD(hour, -48, GETUTCDATE())
AND rsi2.start_time > rsi1.start_time
AND p1.plan_id <> p2.plan_id
AND rs2.avg_duration > rs1.avg_duration * 2
Order By q.query_id, rsi1.start_time, rsi2.start_time
Настройки и информация:
- SQL Server 2016 SP1 CU4 Enterprise в кластере Windows Server 2012R2
- Query Store включен и настроен по умолчанию (настройки не изменены)
- база данных импортирована из экземпляра SQL 2005 (и все еще на уровне совместимости 100)
Эмпирическое наблюдение:
- из-за крайне дурацкой статистики мы взяли все объекты * plan_persist **, использованные в неверно оцененном плане (еще нет фактического плана, потому что запрос не был выполнен), и проверили статистику, некоторые индексы, используемые в плане, не имели статистики (DBCC SHOWSTATISTICS ничего не возвращал, выбор из sys.stats показал функцию NULL stats_date () для некоторых индексов
Быстрое и грязное решение:
- вручную создать недостающую статистику по системным объектам, связанным с Query Store или
- принудительно выполнить запрос, используя новый CE (traceflag), который также создаст / обновит необходимую статистику или
- измените уровень совместимости базы данных на 130 (по умолчанию будет использоваться новый CE)
Итак, мой настоящий вопрос будет:
Почему запрос в хранилище запросов может вызвать проблемы с производительностью всего экземпляра? Мы находимся на территории с ошибками в Query Store?
PS: Я скоро выложу несколько файлов (экраны печати, статистику ввода-вывода и планы).
Файлы добавлены в Dropbox .
План 1 - начальный дурацкий расчетный план в производстве
План 2 - фактический план, старый CE, в тестовой среде (такое же поведение, та же дурацкая статистика)
План 3 - фактический план, новый CE, в тестовой среде
Ответы:
Как я сказал в ответе, эмпирический тест показал, что существуют индексы для системных объектов sys.plan_persisted * без какой-либо (никакой) статистики, созданной над ними. Я подозреваю, что это связано с тем, что база данных была перенесена из экземпляра SQL 2005 и некоторое время поддерживалась на уровне совместимости 100, поэтому новый CE не использовался.
Проверка количества строк:
Это показало, что первоначальные оценки были неверными. Выполнено с подключением к ЦАП, в противном случае таблицы недоступны для запроса.
Статистика проверки:
Это показало, что некоторые индексы имели пустую статистику (отсутствует, нет, ноль).
Начальное исправление:
Этот вид исправил статистику и завершил запрос за 10-12 секунд.
Второе исправление :
(проверено только в тестовой среде) и, скорее всего, правильным, поскольку он показал лучшую статистику для запроса, было изменение уровня совместимости базы данных до 130. Конечным результатом было то, что запрос завершился примерно через 10-12 секунд с нормальная числовая статистика (10 тыс. строк).
Промежуточное исправление :
Некоторая связанная справка о статистике по системным скрытым таблицам.
источник
Основная проблема, которая видна, если вы откроете фактический план в SSMS и посмотрите на использование ЦП (или изучите XML), это узел 32, TVF. Причиной медленных запросов в Query Store является повторный доступ к TVF в памяти .
Неважно, сколько строк возвращено из этих TVF, только количество обращений к ним. Исправление будет тем, что вы можете сделать, чтобы оттолкнуть ваши планы от прочтения их несколько раз.
Исходя из моей ограниченной отладки (как по навыкам, так и по затраченному времени), моя гипотеза состоит в том, что вся память, выделенная для конкретного компонента в памяти данных хранилища запросов, сканируется при каждом выполнении TVF. Я не был в состоянии влиять на это распределение памяти либо
sp_query_store_flush_db
илиDBCC FREESYSTEMCACHE
.Успешные обходные пути на данный момент включают в себя руководства по планам, подсказки (
OPTION(HASH JOIN, LOOP JOIN)
пока что мне это хорошо работало) и выполнение запросов к хранилищу запросов на узле AG, доступном только для чтения.источник