Отсутствует некластеризованный индекс, уже являющийся частью кластерного индекса

9

Я отлаживаю медленно выполняющийся запрос, и в плане выполнения предлагается некластеризованный индекс с воздействием 51.6648. Однако некластеризованный индекс включает только столбцы, которые уже находятся в составном кластерном индексе с первичным ключом (PK).

Может ли это быть из-за порядка столбцов в индексе? т. е. если столбцы в кластеризованном индексе не расположены в порядке от наиболее избирательного к наименьшему, то существует ли потенциал для некластеризованного индекса для повышения производительности?

Кроме того, некластеризованный индекс содержит только два из трех столбцов PK, а третий добавляется как включенный столбец. Является ли includeеще одна причина, почему использование некластеризованного индекса может быть более оптимальным?

Ниже приведен пример структур таблиц, с которыми я работаю:

Tables-

Retailers (
    RetailerID int PK, 
    name ...)

Retailer_Relation_Types (
    RelationType smallint PK, 
    Description nvarchar(50) ...)

Retailer_Relations (
    RetailerID int PK FK, 
    RelatedRetailerID int PK FK, 
    RelationType smallint PK FK, 
    CreatedOn datetime ...)

Таблица Retailer_Relationsимеет следующий составной индекс PK и предлагаемый индекс:

CONSTRAINT PK_Retailer_Relations 
PRIMARY KEY CLUSTERED (
    RetailerID ASC, 
    RelatedRetailerID ASC, 
    RelationType ASC
    ) ON [PRIMARY]

CREATE NONCLUSTERED INDEX <NameOfIndex> 
ON Retailer_Relations (
    RetailerID, 
    RelationType
    ) 
INCLUDE (
    RelatedRetailerID
    )
Флетч
источник

Ответы:

12

Таблица Retailer_Relations имеет следующий составной индекс PK и предлагаемый индекс-

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

Точнее, эти подсказки индекса основаны на предпосылке снижения стоимости Query Bucks ™, используемых операторами в плане. Оптимизатор вычисляет предполагаемые затраты и соответственно добавляет недостающие подсказки индекса.

В результате они могут быть очень неправы. Если вы не уверены, поможет ли это, лучше всего проверить ситуацию до и после. Вы можете сделать это, добавив оператор SET STATISTICS IO, TIME ON;перед выполнением запроса.

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

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

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

SELECT  RelatedRetailerID
FROM Retailer_Relations 
WHERE
RetailerID = 5 AND
RelationType = 20;

или вот так:

SELECT  RelatedRetailerID
FROM Retailer_Relations 
ORDER BY
RetailerID,
RelationType;

Причиной этого является то, что оба индекса могут искать по RetailerID, эта часть не изменится. Но что, если к RelationType применяются дополнительные фильтры / упорядочение? Это будет повсеместно в кластерном индексе, так как это будет третье значение ключа, а не второе значение ключа. И, как мы знаем, это второе ключевое значение в NCI.

Хорошо, но когда или как некластеризованный индекс улучшит запрос?

Пара случаев может быть:

  • Если отношение атрибутов отфильтровывает много значений, остаточный ввод-вывод может быть высоким, что может привести к необходимости некластеризованного индекса (запрос № 1).
  • Происходит упорядочение по двум столбцам (в одну сторону), а набор результатов большой (запрос № 2).
  • Как упомянул @AaronBertrand: если разница в размере CI по сравнению с NCI значительна, добавление NCI уменьшит количество страниц, читаемых запросами, которые получают от этого выгоду.

Примечание стороны NCI

В качестве примечания, добавление ключевых столбцов в список включений в NCI точно не требуется, поскольку ключевые столбцы CI автоматически включаются во все некластеризованные индексы.

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

Что касается самого запроса, если бы вы добавили план выполнения через PasteThePlan, мы могли бы дать больше информации об индексации / улучшении запроса.


тестирование

Создать таблицу и добавить несколько строк

CREATE TABLE Retailer_Relations (
    RetailerID int , 
    RelatedRetailerID int , 
    RelationType smallint, 
    CreatedOn datetime,
    CONSTRAINT PK_Retailer_Relations 
PRIMARY KEY CLUSTERED (
    RetailerID ASC, 
    RelatedRetailerID ASC, 
    RelationType ASC
    ) ON [PRIMARY])


    DECLARE @I Int = 1
    WHILE @I < 1000
    BEGIN
    INSERT INTO Retailer_Relations(RetailerID,RelatedRetailerID,RelationType,CreatedOn)
    VALUES(@I,@I,@I,GETDATE()
    )
    set @I += 1
    END

Запрос № 1

    SELECT  RelatedRetailerID
FROM Retailer_Relations 
WHERE
RetailerID = 5 AND
RelationType = 20;

План без индекса Здесь

Пока он выполняет поиск, он выполняет поиск по RetailerID. После этого он выдает остаточный предикат ввода / вывода для RelationType

Добавить индекс

CREATE NONCLUSTERED INDEX IX_TEST
ON Retailer_Relations (
    RetailerID, 
    RelationType
    ) 
INCLUDE (
    RelatedRetailerID
    )

Остаточный предикат пропал, все происходит в предикате поиска в обоих столбцах.

План выполнения

Со вторым запросом добавленная полезность индекса становится еще более очевидной:

SELECT  RelatedRetailerID
FROM Retailer_Relations 
ORDER BY
RetailerID,
RelationType;

План без индекса, с оператором сортировки:

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

План с индексом, с помощью индекса удаляется оператор сортировки

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

Рэнди Вертонген
источник
1
Спасибо, Рэнди, я отмечу это как ответ, но просто хотел спросить, говоришь ли ты, что предложение по отсутствующему индексу основано на плане предполагаемого выполнения? Я спрашиваю об этом, поскольку это отображается в Плане фактического выполнения в SS2016.
Флетч
1
Интересно, это то, что вы говорите, спасибо за разъяснения.
Флетч