Производительность SQL - индексированное представление VS многостолбцовый индекс - PullRequest
0 голосов
/ 25 ноября 2018

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

TLTR: Почему я теряю логарифмическую масштабируемость моего индекса из нескольких столбцов?

таблица:

CREATE TABLE Schema1.Item
(
    Id INT IDENTITY(1,1) PRIMARY KEY,
    UniqueName VARCHAR(20) NOT NULL UNIQUE,
    GroupId INT NOT NULL FOREIGN KEY REFERENCES Schema1.Group(Id),
    Category VARCHAR(200),
    Properties VARCHAR(max)
);

В последнем столбце «Свойства» содержится словарь JSON, если имя-свойства + значение-свойства.Какие свойства там есть, зависит от GroupId.

Тестовые данные:

  • состоит из 1 миллиона элементов
  • , распределенных по 20 группам (таким образом, 50000 элементов наgroup)
  • , которые содержат 10 категорий (т. е. 5000 элементов на категорию на группу)

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

CREATE NONCLUSTERED INDEX IX_GroupId_Category 
ON [Schema1].[Item] (GroupId, Category) 
INCLUDE(Id, UniqueName, Properties)

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

SELECT TOP (1000) *   
FROM [Schema1].[Item]
WHERE GroupId = 2
  AND Category = 'Category4'
  AND JSON_VALUE(Properties, '$."PropertyName"') LIKE '%PropertyValue%'

Но я хочу обсудить только ЭТОТ запрос, потому что в конечном итоге все ПОСЛЕ этого запроса всегда <5000 элементов: </p>

SELECT TOP (1000) *   
FROM [Schema1].[Item]
WHERE GroupId = 2
  AND Category = 'Category4'

План выполнения в основном состоит из 100% поиска по индексу, с предполагаемым + фактическим количеством строк = 1000 (как и ожидалось).Здесь все выглядит хорошо.

Но с 1.000.000 элементов этому запросу все еще нужно 2-3 секунды для завершения (без кэширования запроса).Для 100 000 элементов это было <1 секунда </strong>.

Это, кажется, противоречит логике логарифмической масштабируемости индексов?Даже с моими очень большими листами индекса (поскольку они содержат весь столбец с nvarchar(max), который обычно составляет около 500 байт), все равно не должно быть такой большой разницы между 100 000 и 1 000 000 элементов?

Далее я попытался создать индексированное представление, которое

  • фильтрует по GroupId (таким образом, оно имеет максимум 50 000 строк)
  • и имеет индекс по категории (+ включая всестолбцы, такие же, как и раньше)

И для этого представления запросы вроде этого:

SELECT TOP (1000) *   
FROM [Schema1].[Item_ViewGroupId1]    
WHERE Category = 'Category4'

нужны только <1 секунда </strong>!

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

Я что-то упустил?


РЕДАКТИРОВАТЬ: Кажется, проблема вбыть связано с физическими показаниями:

  • Медленно: Таблица «Элемент».Сканирование 1, логическое чтение 362, физическое чтение 148, чтение с опережением 547, чтение логического объекта 0, чтение с физического объекта 0, чтение с опережением 0.
  • Fast: Таблица «Элемент».Сканирование 1, логическое чтение 362, физическое чтение 0, чтение с опережением 264, логическое чтение с лоба 0, физическое чтение с лоба 0, чтение с опережением чтения 0

И кажется, что в среднемзапросам на представления реже требуется физическое чтение?

Значит ли это, что я просто зависел от того, что сервер кэширует?Можно ли как-нибудь улучшить это?

1 Ответ

0 голосов
/ 25 ноября 2018

Если план такой же, а логический ввод-вывод одинаков, то истекшее время будет одинаковым, если только не будет ожиданий, таких как ожидания ввода-вывода, ожидания блокировки и т. Д. Может случиться так, что при тестировании с большимтаблица, ваши страницы не все кэшированы.

Отслеживание хранилища запросов ожидает на основе запроса и плана, поэтому вы можете проверить что-то вроде:

select qt.query_sql_text, p.plan_id, ws.wait_category, ws.wait_category_desc, ws.avg_query_wait_time_ms
from sys.query_store_query q
left join sys.query_store_query_text qt
  on q.query_text_id= qt.query_text_id
left join sys.query_store_plan p
  on q.query_id = p.plan_id
left join sys.query_store_wait_stats ws
 on p.plan_id = ws.plan_id
order by q.query_id, p.plan_id, ws.wait_category, ws.wait_category_desc, ws.avg_query_wait_time_ms desc

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

Можно ли как-нибудь улучшить это?

SQL Server сохранит наиболее часто используемые страницы в кеше страниц,и для кэширования большего количества данных вы можете либо увеличить объем доступной памяти (увеличивая свои DTU или vCores), либо увеличить количество строк, которые вы помещаете на странице.Здесь можно попробовать СЖАТИЕ данных JSON и ДЕКОМПРЕСС при необходимости.Это позволит кэшировать больше данных за счет дополнительного процессора при чтении.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...