Sql Server: Производительность При Вставке Данных В Таблицу С Кластерным Индексом И Без Него

Привет, Хабр! Представляю вашему вниманию перевод статьи Производительность вставки SQL Server для кластерных индексов и таблиц кучи



Вопрос

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

Большинство источников отмечают административные преимущества использования кластерных индексов.

Но оказывает ли это какое-либо влияние на производительность и другие положительные или отрицательные аспекты?

Решение

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

Ниже приведен скрипт для создания тестовых таблиц и заполнения их данными.

Скрипт 1

  
  
   

-- Create table and indexes CREATE TABLE testtable ([col1] [int] NOT NULL PRIMARY KEY CLUSTERED, [col2] [int] NULL, [col3] [int] NULL, [col4] [varchar](50) NULL, [col5] uniqueidentifier); -- Load sample data into table DECLARE @val INT SELECT @val=1 WHILE @val < 5000000 BEGIN INSERT INTO testtable (col1, col2, col3, col4, col5) VALUES (@val,round(rand()*100000,0), round(rand()*100000,0),'TEST' + cast(@val AS VARCHAR), newid()) SELECT @val=@val+1 END GO -- Create sample table and indexes CREATE TABLE testtable2 ([col1] [int] NOT NULL PRIMARY KEY NONCLUSTERED, [col2] [int] NULL, [col3] [int] NULL, [col4] [varchar](50) NULL, [col5] uniqueidentifier); INSERT INTO testtable2 SELECT * FROM testtable;

Первое, что я сразу заметил, это то, что стопка занимает больше места, потому что.

таблица и индекс — это отдельные структуры.

В кластеризованном индексе данные хранятся в листах индекса и поэтому занимают меньше места.

Ниже приведена таблица, показывающая объем пространства, используемого каждым объектом после загрузки данных.

Стол Индекс Использовано (КБ) Зарезервировано (КБ) Количество строк
таблица испытаний PK__testtabl__357D0D3E3D086A66 257952 257992 4999999
тестовая таблица2 КУЧА 256992 257032 4999999
тестовая таблица2 PK__testtabl__357D0D3F2CBA35D8 89432 89608 4999999
После загрузки первичных данных перейдем ко второму этапу загрузки и посмотрим на производительность с помощью SQL Profiler, а также проверим используемое пространство.

Ниже приведен скрипт, который загружает в каждую таблицу дополнительно 100 000 записей.

Скрипт 2

-- insert when all pages are full DECLARE @val INT SELECT @val=5000000 WHILE @val < 5100000 BEGIN INSERT INTO testtable (col1, col2, col3, col4, col5) VALUES (@val,round(rand()*100000,0), round(rand()*100000,0),'TEST' + cast(@val AS VARCHAR), newid()) SELECT @val=@val+1 END GO DECLARE @val INT SELECT @val=5000000 WHILE @val < 5100000 BEGIN INSERT INTO testtable2 (col1, col2, col3, col4, col5) VALUES (@val,round(rand()*100000,0), round(rand()*100000,0),'TEST' + cast(@val AS VARCHAR), newid()) SELECT @val=@val+1 END GO

Для начала посмотрим на используемое пространство, которое, как и ожидалось (ведь страницы были заполнены до конца и ничего не удалено), увеличилось пропорционально для каждого объекта.

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

Стол Индекс Использовано (КБ) Зарезервировано (КБ) Количество строк
таблица испытаний PK__testtabl__357D0D3E3D086A66 263128 263176 5099999
тестовая таблица2 КУЧА 262392 262472 5099999
тестовая таблица2 PK__testtabl__357D0D3F2CBA35D8 91216 91272 5099999
Но SQL Profiler показывает нам более интересные вещи.

Т.

к.

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

таблицу с кластеризованным индексом, поэтому это займет немного больше времени.

Тип индекса ЦП (мс) Операции чтения Операции записи Длительность (мс)
Кластеризованный 3500 304919 654 11288
Куча 3890 406083 904 11438
Теперь мы случайным образом удалим некоторые данные и вставим еще 100 000 записей, чтобы увидеть, как наличие пробелов на некоторых страницах влияет на производительность.

Сценарий, выполняющий удаление и вставку, приведен ниже.

Скрипт 3

-- remove 1000000 random records from each table DELETE FROM testtable WHERE col1 in (SELECT TOP 1000000 col1 FROM testtable ORDER BY newid()); DELETE FROM testtable2 WHERE col1 not in (SELECT col1 FROM testtable); GO -- insert when there is free space in pages DECLARE @val INT SELECT @val=5100000 WHILE @val < 5200000 BEGIN INSERT INTO testtable (col1, col2, col3, col4, col5) VALUES (@val,round(rand()*100000,0), round(rand()*100000,0),'TEST' + cast(@val AS VARCHAR), newid()) SELECT @val=@val+1 END GO DECLARE @val INT SELECT @val=5100000 WHILE @val < 5200000 BEGIN INSERT INTO testtable2 (col1, col2, col3, col4, col5) VALUES (@val,round(rand()*100000,0), round(rand()*100000,0),'TEST' + cast(@val AS VARCHAR), newid()) SELECT @val=@val+1 END GO

На этот раз SQL Profiler показывает увеличение разрыва в производительности между двумя таблицами в пользу кластеризованного индекса.

Тип индекса ЦП (мс) Операции чтения Операции записи Длительность (мс)
Кластеризованный 3562 304859 653 10334
Куча 4973 422142 7053 13042
Скорее всего, такая разница возникла из-за того, что при вставке записей в кучу СУБД будет искать на каждой странице пустое место для размещения данных.

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

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

Также стоит обратить внимание на пространство, занимаемое столами.

В случае с кучей при повторном использовании пространства объем занимаемого таблицей места не должен увеличиваться, ведь мы вставили столько же, сколько удалили.

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

Имя таблицы Имя индекса Использовано (КБ) Зарезервировано (КБ) Количество строк
таблица испытаний PK__testtabl__357D0D3E3D086A66 268304 268360 4199999
тестовая таблица2 КУЧА 262392 262472 4199999
тестовая таблица2 PK__testtabl__357D0D3F2CBA35D8 93008 93064 4199999


Заключение

В этом исследовании мы убедились, что использование кластеризованного индекса по сравнению с кучей дает лучшие результаты во всех измеряемых категориях: ЦП, ввод-вывод и время выполнения.

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

Это пространство можно уменьшить, перестроив индекс.

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

Теги: #sql #sql-сервер #индекс #производительность #оптимизация #настройка #базы данных #индексы #производительность #оптимизация #sql #Microsoft SQL Server

Вместе с данным постом часто просматривают: