У меня есть следующая таблица с 7,5 миллионами записей:
CREATE TABLE [dbo].[TestTable](
[Id] [int] IDENTITY(1,1) NOT NULL,
[TestCol] [nvarchar](50) NOT NULL,
[TestCol2] [nvarchar](50) NOT NULL,
[TestCol3] [nvarchar](50) NOT NULL,
[Anonymised] [tinyint] NOT NULL,
[Date] [datetime] NOT NULL,
CONSTRAINT [PK_TestTable] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Я заметил, что когда в поле даты есть некластеризованный индекс:
CREATE NONCLUSTERED INDEX IX_TestTable_Date ON [dbo].[TestTable] ([Date])
-и я запускаю следующий запрос:
UPDATE TestTable
SET TestCol='*GDPR*', TestCol2='*GDPR*', TestCol3='*GDPR*', Anonymised=1
WHERE [Date] <= '25 August 2016'
- поступающие данные, возвращаемые операцией доступа к индексу, сортируются в соответствии с порядком клавиш PK / CX, что снижает производительность.
Я был удивлен, обнаружив, что удаление индекса из поля даты фактически повышает производительность запроса примерно на 30%, поскольку он больше не выполняет сортировку:
Моя теория, и это может быть очевидно для более опытных из вас, заключается в том, что она выяснила, что столбец даты неявно упорядочен точно так же, как первичный ключ / кластерный индекс.
Поэтому мой вопрос: возможно ли воспользоваться этим фактом для повышения производительности моего запроса?
источник
[Date]
но поDESC
порядку? Просто любопытно, так как предикат есть<=
. Кроме того, если индекс onDate
(по умолчанию,ACS
order) помогает другим запросам, то, возможно, вы можете попробовать добавить табличную подсказку в UPDATE, чтобы заставить его использовать PK? Или, может быть, разбить это на две части: создать временную таблицу, заполнить[Id]
на основе[Date] <= '25 August 2016'
, а затем удалитьWHERE
из ОБНОВЛЕНИЕ и добавитьFROM dbo.TestTable tt INNER JOIN #tmp ids ON ids.[Id] = tt.[Id]
. В конце концов, это ОБНОВЛЕНИЕ, и оно должно найти фактические строки, индекс или нет.Ответы:
Я смоделировал тестовые данные, которые в основном воспроизводят вашу проблему:
Статистика для запроса, который использует некластеризованный индекс:
Статистика для запроса, использующего кластерный индекс:
Как добраться до вашего вопроса:
Да. Вы можете использовать некластеризованный индекс, который вам уже
id
нужен , чтобы эффективно найти максимальное значение, которое необходимо обновить. Если вы сохраните это в переменной и отфильтруете ее, вы получите план запроса для обновления, которое выполняет сканирование кластерного индекса (без сортировки), которое останавливается рано и, следовательно, выполняет меньше операций ввода-вывода. Вот одна из реализаций:Запустите статистику для нового запроса:
А также план запроса:
С учетом всего вышесказанного, ваше желание ускорить запрос подсказывает мне, что вы планируете выполнять запрос более одного раза. Прямо сейчас у вашего запроса есть открытый фильтр в
date
столбце. Действительно ли необходимо анонимизировать строки более одного раза? Можете ли вы избежать обновления или сканирования уже анонимных строк? Конечно, должно быть быстрее обновить диапазон дат с датами по обе стороны от него. Вы также можете добавитьAnonymised
столбец в свой индекс, но этот индекс необходимо будет обновить во время вашегоUPDATE
запроса. Таким образом, по возможности, избегайте повторной обработки одних и тех же данных.Исходный запрос, который у вас есть с сортировкой, медленнее из-за работы, выполняемой в
Clustered Index Update
операторе. Время, затраченное на поиск и сортировку индекса, составляет всего 407 мс. Вы можете увидеть это в фактическом плане. План выполняется в режиме строки, поэтому время, затраченное на сортировку, равно времени этого оператора вместе с каждым дочерним оператором:Это оставляет оператору сортировки около 1600 мс времени. SQL Server должен прочитать страницы из кластерного индекса, чтобы выполнить обновление. Вы можете видеть, что
Clustered Index Update
оператор выполняет 1205921 логическое чтение. Вы можете прочитать больше о сортировке оптимизированная для DML и оптимизированный предвыборку в этом блоге по Пол Уайт .Другой план запроса (без сортировки) занимает 683 мс для сканирования кластерного индекса и около 550 мс для
Clustered Index Update
оператора. Оператор обновления не выполняет ввода-вывода для этого запроса.Простой ответ о том, почему план с сортировкой медленнее, заключается в том, что SQL Server выполняет больше логических операций чтения кластерного индекса для этого плана по сравнению с планом сканирования кластерного индекса. Даже если все необходимые данные находятся в памяти, затраты на выполнение этих логических чтений все равно остаются высокими. Гораздо труднее получить лучший ответ, поскольку, насколько я знаю, планы не дадут вам дальнейших подробностей. Для сравнения стеков вызовов между запросами можно использовать PerfView или другой инструмент, основанный на трассировке ETW:
Слева находится запрос, который выполняет сканирование кластерного индекса, а справа - запрос, который выполняет сортировку. Я пометил стеки вызовов синим или красным цветом, которые появляются только в одном запросе. Неудивительно, что различные стеки вызовов с большим числом выбранных циклов ЦП для запроса сортировки, по-видимому, связаны с логическими чтениями, необходимыми для выполнения обновления кластерного индекса. Кроме того, существуют различия в количестве циклов выборки между запросами для одной и той же операции. Например, запрос с сортировкой тратит 31 цикл на получение защелок, тогда как запрос на сканирование тратит только 9 циклов на получение защелок.
Я подозреваю, что SQL Server выбирает более медленный план из-за ограничения стоимости оператора плана запроса. Возможно, часть различий во времени выполнения связана с аппаратным обеспечением или вашей версией SQL Server. В любом случае, SQL Server не может определить, что столбец даты неявно упорядочен точно так же, как кластерный индекс. Данные возвращаются из сканирования кластерного индекса в порядке кластеризованных ключей, поэтому нет необходимости выполнять сортировку в попытке оптимизировать ввод-вывод при обновлении кластерного индекса.
источник