SELECT TOP 1 из очень большой таблицы в столбце индекса выполняется очень медленно, но не в обратном порядке («desc»)

17

У нас есть большая база данных, около 1 ТБ, на которой работает SQL Server 2014 на мощном сервере. Все работало нормально в течение нескольких лет. Около 2 недель назад мы провели полное обслуживание, которое включало: установку всех обновлений программного обеспечения; перестройте все индексы и компактные файлы БД. Однако мы не ожидали, что на определенном этапе загрузка ЦП БД увеличилась более чем на 100% до 150%, когда фактическая нагрузка была одинаковой.

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

select top 1 EventID from EventLog with (nolock) order by EventID

Это всегда занимает около 1,5 секунд! Однако подобный запрос с «desc» всегда занимает около 0 мс:

select top 1 EventID from EventLog with (nolock) order by EventID desc

PTable имеет около 500 миллионов строк; EventIDявляется основным столбцом кластеризованного индекса (упорядоченным ASC) с типом данных bigint (столбец Identity). Существует несколько потоков, вставляющих данные в таблицу вверху (большие EventID), и 1 поток, удаляющий данные снизу (меньшие EventID).

В SMSS мы убедились, что два запроса всегда используют один и тот же план выполнения:

  • Сканирование кластерного индекса;

  • Предполагаемые и фактические номера строк равны 1;

  • Предполагаемое и фактическое количество казней равно 1;

  • Ориентировочная стоимость ввода / вывода 8500 (кажется высокой)

  • При последовательном запуске стоимость запроса равна 50% для обоих.

Я обновил статистику индекса with fullscan, проблема сохранилась; Я снова перестроил индекс, и проблема, казалось, ушла на полдня, но вернулась.

Я включил статистику ввода-вывода с:

set statistics io on

затем выполнил два запроса последовательно и нашел следующую информацию:

(Для первого запроса, медленный)

Таблица «PTable». Сканирование 1, логическое чтение 407670, физическое чтение 0, чтение с опережением 0, логическое чтение 1, физическое чтение 1, чтение с опережением 0.

(Для второго запроса самый быстрый)

Таблица «PTable». Сканирование 1, логическое чтение 4, физическое чтение 0, чтение с опережением 0, логическое чтение с 0, физическое чтение с 0, чтение с опережением 0.

Обратите внимание на огромную разницу в логическом чтении. Индекс используется в обоих случаях.

Фрагментация индекса может объяснить немного, но я считаю, что влияние очень мало; и проблема никогда не случалась раньше. Еще одним доказательством является то, что я запускаю запрос вроде

select * from EventLog with (nolock) where EventID=xxxx   

Даже если я установлю xxxx наименьшим EventID в таблице, запрос всегда будет молниеносным.

Мы проверили, и нет проблем с блокировкой / блокировкой.

Примечание: я просто попытался упростить проблему выше. «PTable» на самом деле является «EventLog»; PIDесть EventID.

Я получаю тот же результат тестирования без NOLOCKподсказки.

Кто-нибудь может помочь?

введите описание изображения здесь

введите описание изображения здесь

Более подробные планы выполнения запросов в XML выглядят следующим образом:

https://www.brentozar.com/pastetheplan/?id=SJ3eiVnob

https://www.brentozar.com/pastetheplan/?id=r1rOjVhoZ

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

Таблица была создана обычно со EventIDстолбцом в качестве первичного ключа, который является identityстолбцом типа bigint. В настоящее время, я думаю, проблема в фрагментации индекса. Сразу после перестройки индекса проблема, казалось, ушла на полдня; но почему он вернулся так быстро ...?

TiffanyP
источник

Ответы:

18

Сканирование кластеризованного индекса показывает 423 723 логических чтения для возврата первой строки, что занимает 1926 мс:

NUTS

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

Скорее всего, ваша задача очистки призраков выполняется далеко позади или остановлена. Вы должны проверить ghost_record_countналичие кластерного индекса sys.dm_db_index_physical_statsи отслеживать изменения с течением времени.

Заказало сканирование с конца индекса , который видит постоянные удаления будут сканировать через очень много пунктирные записи , прежде чем он находит первую «живую» строку возвращение. Это объясняет дополнительные логические чтения. Поиск по b-дереву к наименьшему значению индекса приведет к гораздо меньшему количеству призрачных записей.

Другим фактором, влияющим на производительность, является то, что само сканирование становится ответственным за удаление записей о призраках, как упомянуто в книге «Внутри хранилища: тщательная очистка призраков » Пола Рэндала.

Вы должны проверить, что флаг трассировки 661 (отключить очистку от призраков) не активен.

Решения

  • Вы можете обнаружить, что запуск sp_clean_db_free_space обеспечивает облегчение.
  • Изменение процесса, который удаляет строки из этого конца индекса, чтобы использовать PAGLOCKподсказку, включило бы немедленную очистку на месте , что также могло бы решить проблему.

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

В вашем конкретном случае:

Оказалось, что проблема была вызвана другой тестовой базой данных на том же сервере. Эта тестовая база данных была восстановлена ​​с «потерей данных» и повреждена. Удивительно, но процесс очистки от привидений застрял в этой базе данных. После того, как мы удалили эту поврежденную базу данных из SMSS, проблема решилась сама собой (это заняло много времени и могло привести к блокировке БД на короткое время).

Пол Уайт восстановил Монику
источник