Skip to content

Как думать подобно SQL Server: добавить некластеризованный индекс

Пересказ статьи Brent Ozar. How to Think Like the SQL Server Engine: Adding a Nonclustered Index


Прочитав последнюю статью, наши пользователи продолжали выполнять этот запрос и хотели бы его ускорить:
SELECT Id 
FROM dbo.Users
WHERE LastAccessDate > '2014/07/01'
ORDER BY LastAccessDate;

Давайте подготовим данные, создав копию таблицы, отсортированную так, чтобы мы могли быстро найти требуемые строки:

CREATE INDEX IX_LastAccessDate_Id
ON dbo.Users(LastAccessDate, Id);

Этим мы создадим отдельную копию нашей таблицы (также сохраняемой на 8-килобайтных страницах), которая выглядит следующим образом:



Этот индекс замедляет вставку и удаление.


Первое, что следует иметь в виду, это то, что теперь вы имеете две физических копии таблицы. Всякий раз, когда вы вставляете новую строку, вы должны добавить её в два места. Всякий раз, когда вы удаляете строку, вы должны убрать её из двух мест. Вы фактически удвоили число записей, которое должно обслуживать ваше хранилище. (Обновление несколько хитрее, о чем пойдет речь в следующей статье.)

Во-вторых, некластеризованный индекс (черная страница) является более плотным: на 8-килобайтную страницу может поместиться больше пользователей, поскольку мы сохраняем в индексе меньше полей. Вы можете увидеть это выполнив sp_BlitzIndex, если посмотрите на верх результирующего набора, где показано, насколько велик каждый индекс:



Кластеризованный индекс (CX PK) имеет около 300К строк и занимает 58,1Мб на диске, поскольку он содержит все столбцы таблицы.

Некластеризованный индекс содержит то же число строк, но занимает только 5,3Мб места на диске, поскольку хранит только LastAccessDate и Id. Чем больше столбцов вы добавляете в индекс - в ключ или же во включенные столбцы - тем больше места он занимает на диске. (Я расскажу еще о проектировании индексов по ходу этой серии.)

Но этот индекс с лихвой окупается при выборке.


Снова испытайте наш запрос на выборку. Здесь я выполняю подряд два запроса: первый с хинтом INDEX=1, чтобы показать стоимость сканирования кластерного индекса. (Index #1 - это ваш кластеризованный индекс.)



Наш новый план внизу значительно проще:

  • Мы получаем поиск (seek), а не сканирование (scan), поскольку SQL Server имеет возможность перейти непосредственно к строкам, у которых LastAccessDate > 2014/07/01. Это не требует сканирования всего объекта.

  • Нам не требуется сортировка, поскольку мы считываем данные, которые уже отсортированы по LastAccessDate.


Стоимость поиска по индексу ниже по двум причинам: ему не нужна сортировка, и он читает меньше 8-килобайтных страниц. Чтобы увидеть насколько меньше, перейдите на вкладку сообщений:



Сканирование кластеризованного индекса читает 7405 страниц и занимает 100мс времени процессора, чтобы выполнить ORDER BY.

Новый некластеризованный индекс читает только 335 страниц - примерно в 20 раз меньше - и не требует времени процессора на ORDER BY. Действительно хорошее проектирование индексов, подобное данному, это то, что позволит вам быстро получить улучшение в 20 или много более раз без переписывания ваших запросов. Хотел бы я, чтобы каждый запрос был идеально настроен? Конечно, но даже если бы это было так, вам все равно нужно будет помочь движку, организовав данные более удобным для поиска способом.

Этот индекс называется покрывающим.


Индекс идеально покрывает данный запрос, удовлетворяя его потребностям быстрым, эффективным способом. Термин "покрывающий" не является обозначением индексов особого вида, который создаете с использованием специального синтаксиса. "Покрывающий" просто относится к комбинации индекса И данного запроса. Если вы измените запрос - как мы собираемся сделать здесь в ближайшее время - то индекс может перестать быть покрывающим, и нам предстоит опять проделать больше работы.

Итак, наш план имеет поиск по индексу - это лучший план, который можно получить, верно? Нет, как мы увидим в следующем эпизоде.

Обратные ссылки

Нет обратных ссылок

Комментарии

Показывать комментарии Как список | Древовидной структурой

Нет комментариев.

Автор не разрешил комментировать эту запись

Добавить комментарий

Enclosing asterisks marks text as bold (*word*), underscore are made via _word_.
Standard emoticons like :-) and ;-) are converted to images.

To prevent automated Bots from commentspamming, please enter the string you see in the image below in the appropriate input box. Your comment will only be submitted if the strings match. Please ensure that your browser supports and accepts cookies, or your comment cannot be verified correctly.
CAPTCHA

Form options

Добавленные комментарии должны будут пройти модерацию прежде, чем будут показаны.