Селективный индекс от 1С - что выберет MS SQL?

18.10.22

База данных - HighLoad оптимизация

Большие объемы данных вынуждают глубже смотреть в логику работы оптимизатора СУБД. Ваше понимание о селективном индексе может расходится с логикой оптимизатора SQL. Это еще больше привязывает конкретную систему к конкретной СУБД. Построение хорошего запроса к СУБД попрежнему остается искусством, а искусственный интеллект пока еще туда не добрался

Скачать исходный код

Наименование Файл Версия Размер
План стандартный индекс
.txt 10,85Kb
5
.txt 10,85Kb 5 Скачать бесплатно
План запроса - оптимизатор выбрал нестандартный индекс
.txt 10,56Kb
4
.txt 10,56Kb 4 Скачать бесплатно

Кто он – селективный индекс.

В предыдущей статье Партицированная дисциплина программиста был показан пример запроса на соединение двух таблиц для регистра сведений, и показано как MS SQL выбирает потоки данных для merge join с использованием стандартных  индексов 1С.  В частности было отмечено, что без дополнительных условий в Index seek, в поток для Megre join попадают все записи индекса и приходится указывать дополнительные фильтры для ограничения. Вопрос: почему так происходит? - остался открытым.

Все что описано ниже, это мои выводы на основании анализа поведения оптимизатора MS SQL 2019  в разных условиях. Официально изложенных алгоритмов, которые использует оптимизатор MS SQL я не нашел, если знаете ссылку пишите в комментариях.

Сначала вспомним понятие селективного индекса

Если сказать кратко – индекс для данного запроса является селективным, если при его использовании можно выбрать

  • Больше уникальных строк
  • С меньшим количеством дублей
  • Наименьшее количество строк на каждую комбинацию ключевых значений

Про селективность хорошо написано тут (правда для Oracle, но это же общая концепция) Что такое селективный индекс

 

Возьмем оптимизированный запрос из предыдущей статьи

ВЫБРАТЬ РАЗЛИЧНЫЕ
    СУУ_АгрегированныеДенежныеТранзакции.СвязаннаяОпИдИсхСистемы КАК СвязаннаяОпИдИсхСистемы
ПОМЕСТИТЬ Врем_ИдОперацийИзТранзакций
ИЗ
    РегистрСведений.СУУ_АгрегированныеДенежныеТранзакции КАК СУУ_АгрегированныеДенежныеТранзакции
ГДЕ
    СУУ_АгрегированныеДенежныеТранзакции.Период >= &ДатаНачала

ИНДЕКСИРОВАТЬ ПО
    СвязаннаяОпИдИсхСистемы
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    СУУ_АгрегированнаяСделкаКП.Период,
    СУУ_АгрегированнаяСделкаКП.ИсходнаяСистема,
    СУУ_АгрегированнаяСделкаКП.ИдИсхСистемы КАК ИдИсхСистемы,
    СУУ_АгрегированнаяСделкаКП.ОсновнойСчет,
    СУУ_АгрегированнаяСделкаКП.НогаСделки
ПОМЕСТИТЬ РезультатВыбранныеВерсииСделок
ИЗ
    РегистрСведений.СУУ_АгрегированнаяСделкаКП КАК СУУ_АгрегированнаяСделкаКП
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ Врем_ИдОперацийИзТранзакций КАК Врем_ИдОперацийИзТранзакций
        ПО СУУ_АгрегированнаяСделкаКП.ИдИсхСистемы = Врем_ИдОперацийИзТранзакций.СвязаннаяОпИдИсхСистемы
ГДЕ
    СУУ_АгрегированнаяСделкаКП.Период >= ДОБАВИТЬКДАТЕ(&ДатаНачала, МЕСЯЦ, -3)

И опять его запустим со стандартными индексами 1С. MS SQL нас интересует последний запрос


INSERT INTO #tt3 WITH(TABLOCK) (_Q_001_F_000, _Q_001_F_001RRef, _Q_001_F_002, _Q_001_F_003RRef, _Q_001_F_004RRef) SELECT

T1._Period,

T1._Fld18861RRef,

T1._Fld18865,

T1._Fld18863RRef,

T1._Fld19363RRef

FROM dbo._InfoRg18860 T1 WITH(NOLOCK)

INNER JOIN #tt2 T2 WITH(NOLOCK)

ON (T1._Fld18865 = T2._Q_000_F_000)

WHERE ((T1._Fld628 = @P1)) AND ((T1._Period >= @P2))',N'@P1 numeric(10),@P2 datetime2(3)

Смотрим общую цену, которую зафиксировал оптимизатор -  в попугаях 7767

План получается с Index Seek по типовому индексу _InfoR18860_ByDims18897_STRRRR

 

Структура индекса

 

План запроса ниже, видно, что основная тяжесть ввода вывода идет на Index Seek и операции Merge

 

 

Вроде все хорошо, по правилам и предсказуемо, но давайте добавим ему другой индекс в котором, убрано поле _Fld628 . Это поле содержит 0 поскольку в типовой конфигурации есть, но не используются

 

Смотрим результат. Неожиданно – SQL сервер выбрал новый индекс сам , даже при том что _Fld628 (разделителя) там вообще нет! Хотя есть индекс _InfoR18860_ByDims18897_STRRRR который формально удовлетворяет всем условиям.

 

План при этом получился лучше, но ненамного

 

Разница

  1. По стандартному индексу идет  |--Index Seek(OBJECT:([MIS_PROD2].[dbo].[_InfoRg18860].[_InfoR18860_ByDims18897_STRRRR] AS [T1]), SEEK:([T1].[_Fld628]=[@P1]),  WHERE:([MIS_PROD2].[dbo].[_InfoRg18860].[_Period] as [T1].[_Period]>=[@P2]) ORDERED FORWARD)
  2. По нестандартному индексу идет скан с проверкой всех условий |--Index Scan(OBJECT:([MIS_PROD2].[dbo].[_InfoRg18860].[_InfoR18860_MySuperWithout_Fld628_ByDims18897_STRRRR] AS [T1]),  WHERE:([MIS_PROD2].[dbo].[_InfoRg18860].[_Fld628] as [T1].[_Fld628]=[@P1] AND [MIS_PROD2].[dbo].[_InfoRg18860].[_Period] as [T1].[_Period]>=[@P2]) ORDERED FORWARD)

Мы выбираем селективный индекс, а оптимизатор выбирает …

Почему MS SQL так сделал?

Сначала я подумал так «Скорее всего поле, где _Fld628 = 0 в каждой записи, убивает всю селективность индекса и как только появляется достойная альтернатива, то MS SQL  сам бежит к ней»

Но после обсуждения на Хабре и нескольких экспериментов выяснилось, что дело в количестве IO по индексу

Т.е. по индексу  _InfoR18860_ByDims18897_STRRRR  фактическая статистика показала почти 5 млн чтений

 

 

а по индексу _InfoR18860_MySuperWithoutFld628_ByDims18897_STRRRR около 3 млн чтений, причем через Index Scan. Индекс _InfoR18860_MySuperWithoutFld628_ByDims18897_STRRRR явно не укладывается в критерии селективности, но оптимизатор MS SQL видит, что по нему работать быстрее

 

 

Фрагментация у стандартного индекса действительно была, и после Rebuild оптимизатор опять начал его выбирать как основной! Более того – посмотрите как уменьшилось количество чтений до 3.4 миллиона

 

 

 

Вывод: Селективность для оптимизатора MS SQL это уже не основной показатель и как показано ниже для других СУБД тоже. Оптимизатор MS SQL может отказаться использовать селективный индекс даже при сильной фрагментации. Более того, в статье MS SQL Server: ваши статистики не работают! Так ли все плохо на самом деле? видно, что поле разделителя влияет на качество статистик MS SQL

 

Вопрос А в какое место индекса, тогда поставить это поле _Fld628 (ОбластьДанныхОсновныеДанные) , которое 1С по умолчанию ставит в начало (префикс) всех индексов?

Это сложный вопрос. Если ОбластьДанныхОсновныеДанные используется хотя бы с несколькими значениями, селективность повысится в стандартном индексе, учитывая условия на равенства которые добавляет 1С  T1._Fld628 = @P1 (равенство всегда в приоритете у оптимизатора нежели >= <=)

Но все очень зависит от СУБД , например тут описаны мифы о селективных индексах причем с планами для разных СУБД

Most selective first ?

“The myth is extraordinarily persistent in the SQL Server environment and appears even in the official documentation. The reason is that SQL Server keeps a histogram for the first index column only. But that means that the recommendation should read like “uneven distributed columns first” because histograms are not very useful for evenly distributed columns anyway.”

Т.е. первая колонка в индексе решает все и ее количество уникальных значений. Если там один 0 работа с остальными полями идет уже менее эффективно. Если 0 1 2, то это тоже сильно ситуацию не исправляет, поскольку для Merge без доп условий пойдет поток данных по всему T1._Fld628 = @P1

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

MS SQL Оптимизация индексы селективность highload

См. также

Infostart Toolkit: Инструменты разработчика 1С 8.3 на управляемых формах

Инструментарий разработчика Роли и права Запросы СКД Платформа 1С v8.3 Управляемые формы Запросы Система компоновки данных Конфигурации 1cv8 Платные (руб)

Набор инструментов программиста и специалиста 1С для всех конфигураций на управляемых формах. В состав входят инструменты: Консоль запросов, Консоль СКД, Консоль кода, Редактор объекта, Анализ прав доступа, Метаданные, Поиск ссылок, Сравнение объектов, Все функции, Подписки на события и др. Редактор запросов и кода с раскраской и контекстной подсказкой. Доработанный конструктор запросов тонкого клиента. Продукт хорошо оптимизирован и обладает самым широким функционалом среди всех инструментов, представленных на рынке.

10000 руб.

02.09.2020    127840    689    389    

740

Infostart PrintWizard - создание и редактирование печатных форм в 1С 8.3

Пакетная печать Печатные формы Инструментарий разработчика Платформа 1С v8.3 Запросы 1С:Зарплата и кадры бюджетного учреждения 1С:Конвертация данных 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 Платные (руб)

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

18000 руб.

06.10.2023    8584    25    6    

47

"Штрихкод-информер" - мобильный ТСД и прайс-чекер в смартфоне

Мобильная разработка Сканер штрих-кода Терминал сбора данных Управляемые формы Мобильная платформа 1С:Розница 2 1С:Управление нашей фирмой 1.6 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Управленческий учет Платные (руб)

Сбор заказов, инвентаризация, проверка ценников, просмотр полной информации об остатках и ценах со смартфона Онлайн. Отправка данных со смартфона выполняется либо напрямую в открытую форму документа, отсканировав QR-код, либо в общую корзину учетной системы, не подходя к компьютеру. Кассир или оператор сможет просмотреть список присланных данных и загрузить в любую форму, поддерживающую работу с ТСД. Для работы с мобильным приложением требуется опубликовать HTTP-сервис из поставляемого расширения.

2880 руб.

03.12.2018    55569    166    102    

162

Infostart УДиФ: Управление данными и формами 1С

Инструменты администратора БД Инструментарий разработчика Роли и права Платформа 1С v8.3 Конфигурации 1cv8 Россия Платные (руб)

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

10000 руб.

10.11.2023    4879    13    2    

38

SALE! 25%

Что нам стоит бота построить? Нарисуем - будет жить! Графический конструктор телеграм-ботов/Telegram

Мобильная разработка Мессенджеры и боты Платформа 1С v8.3 Платные (руб)

Теперь создать telegram-бота - элементарно. Достаточно просто нарисовать блок-схему телеграм-бота, и он сразу заработает. Это возможно при использовании Графического конструктора телеграм-ботов. Это единственный конструктор ботов для telegram, чье качество и функционал подтверждены фирмой 1С, есть сертификат 1С:Совместимо. Расширение в интерактивном режиме, с помощью блок-схем, позволяет с минимальными трудозатратами создать телеграм-ботов в любой конфигурации, работающей на платформе «1С:Предприятие 8.3».

13200 9900 руб.

27.12.2021    34174    84    159    

180

SALE! 15%

PowerTools

Инструментарий разработчика Инструменты администратора БД Платформа 1С v8.3 Управляемые формы Конфигурации 1cv8 Россия Платные (руб)

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

3600 3060 руб.

14.01.2013    179122    1086    0    

863

"Мобильный ТСД" - инвентаризация и сбор штрихкодов для iOS и Android

Сканер штрих-кода Терминал сбора данных Мобильная разработка Монитор заказов Оптовая торговля Розничная торговля Ценообразование, анализ цен Платформа 1С v8.3 Мобильная платформа 1С:Розница 2 1С:Управление нашей фирмой 1.6 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Розничная и сетевая торговля (FMCG) Оптовая торговля, дистрибуция, логистика Управленческий учет Платные (руб)

Простой мобильный терминал сбора данных для смартфонов на iOS и Android, не требующий сложных настроек и установки дополнительных программ. Обмен между Вашей 1С и мобильным приложением осуществляется через облачный сервис и расширение конфигурации. Работает с конфигурациями УТ 11, ERP, КА2, Розница 2, Розница 3, УНФ 1.6, УНФ 3.0. Полнофункциональный демо-доступ для своей конфигурации можно запросить в настройках мобильного приложения - все необходимое придет на почту автоматически.

2000 руб.

22.04.2019    92774    527    186    

300

Склад 15 ВЕЩЕВОЙ - автоматизация складов с маркированным товаром

Логистика, склад и ТМЦ Мобильная разработка Платформа 1С v8.3 1С:Управление торговлей 10 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х 1С:Управление нашей фирмой 3.0 Россия Платные (руб)

Программное обеспечение для автоматизации складских операций со встроенными функциями работы с маркированной продукцией. Предназначено для автоматизации всех товароучетных операций на складах, обычных и адресного хранения. «Склад 15 ВЕЩЕВОЙ» объединил в себе несколько товарных групп маркированного товара (ОБУВЬ, ОДЕЖДА, ШИНЫ, ПАРФЮМ, БАДы, ЮВЕЛИРНЫЕ ИЗДЕЛИЯ), которые чаще всего встречаются в складской логистике вместе.

33550 руб.

02.06.2023    3095    2    0    

0
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. redfred 18.10.22 19:45 Сейчас в теме
_Fld628 в included columns ушёл? Можете приложить актуальные планы в xml формате?
2. 1CUnlimited 308 18.10.22 20:08 Сейчас в теме
Никаких included columns нет, В индексе _InfoR18860_MySuperWithoutFld628_ByDims18897_STRRRR поля _Fld628 нет

>Можете приложить актуальные планы в xml формате?
Я в текстовом приложил в конце статьи они хорошо открываются моноширийным шрифтом в 1С . Вам чтото там не хватает ?
XML мне нужно заново гонять запрос.
3. redfred 18.10.22 20:20 Сейчас в теме
(2)
Никаких included columns нет, В индексе _InfoR18860_MySuperWithoutFld628_ByDims18897_STRRRR поля _Fld628 нет


Тогда не очень понятно, как вообще этот индекс используется, если в нём нет поля по которому фильтрация идёт

(2)
Я в текстовом приложил в конце статьи они хорошо открываются моноширийным шрифтом в 1С . Вам чтото там не хватает ?


Да, не хватает, в xml гораздо больше информации содержится. Да и просто смотреть удобнее
4. 1CUnlimited 308 18.10.22 20:43 Сейчас в теме
Ну в принципе в моей статье есть ответ
В обсуждении на хабре (про Пола Вайта) можете увидеть что это бывает при наличии фрагментации
https://habr.com/ru/post/691326/
XML попробую заново получить как будет время, если обсуждение на хабре не снимет вопросы
5. redfred 19.10.22 06:35 Сейчас в теме
(4)
Ну в принципе в моей статье есть ответ
В обсуждении на хабре (про Пола Вайта) можете увидеть что это бывает при наличии фрагментации


Нет, я про другое немного удивлялся - если в индексе в принципе нет поля _Fld628, то фильтрация по нему в принципе не должна работать в этом индексе, соот. не может получиться такой план, как на скринах вижу. Но с утра, на свежую голову, сообразил, что _Fld628 скорее всего PK (или его часть), а значит всё же там присутствует. Впрочем, на хабре про это уже тоже написали

Вывод: Селективность для оптимизатора MS SQL это уже не основной показатель и как показано ниже для других СУБД тоже. Оптимизатор MS SQL может отказаться использовать селективный индекс даже при сильной фрагментации.


Сама по себе селективность индекса - это сферический конь в вакууме, нужно смотреть на селективность предикатов в запросе, а тут уже на одном и том же индексе селективность может очень сильно гулять. А поскольку оптимизатор в mssql стоимостный, то и планы с разными входными параметрами могут быть сильно разные.
Степень фрагментации,емнип, учитывается достаточно опосредованно - напрямую её оптимизатор не видит, но видит количество страниц в индексе. При прочих равных в сильно фрагментированном их будет больше, стоимость узла увеличится, это тоже может повлиять на итоговый план
6. 1CUnlimited 308 19.10.22 11:55 Сейчас в теме
Не знаю где Вы видите первичный ключ в нетрадиционном индексе,
CREATE NONCLUSTERED INDEX [_InfoR18860_MySuperWithout_Fld628_ByDims18897_STRRRR] ON [dbo].[_InfoRg18860]
(
[_Fld18865] ASC,
[_Period] ASC,
[_Fld18861RRef] ASC,
[_Fld18863RRef] ASC,
[_Fld19363RRef] ASC,
[_Fld18862RRef] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ON LINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [AGRREG]
GO

Стандартный индекс он даже привлекательней поскольку уникальный, но никаких Constraints 1С не создает
/****** Object: Index [_InfoR18860_ByDims18897_STRRRR] Script Date: 10/19/2022 11:26:48 AM ******/
CREATE UNIQUE NONCLUSTERED INDEX [_InfoR18860_ByDims18897_STRRRR] ON [dbo].[_InfoRg18860]
(
[_Fld628] ASC,
[_Fld18865] ASC,
[_Period] ASC,
[_Fld18861RRef] ASC,
[_Fld18863RRef] ASC,
[_Fld19363RRef] ASC,
[_Fld18862RRef] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ON LINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [AGRREG]
GO

Кластерный индекс , в данной ситуации не поможет так как там есть поле [_Fld18861RRef] ASC, -- ИсходнаяСистема , которое в нашем запросе не используется, а оно его разрывает

/****** Object: Index [_InfoR18860_ByPeriod_TRSRRR] Script Date: 10/19/2022 11:28:15 AM ******/
CREATE UNIQUE CLUSTERED INDEX [_InfoR18860_ByPeriod_TRSRRR] ON [dbo].[_InfoRg18860]
(
[_Fld628] ASC,
[_Period] ASC, --Период
[_Fld18861RRef] ASC, -- ИсходнаяСистема
[_Fld18865] ASC, --ИдИсходной системы
[_Fld18863RRef] ASC,
[_Fld19363RRef] ASC,
[_Fld18862RRef] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ON LINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [AGRREG]
GO

Что подразумевается под селективностью предикатов? Rows в фактическом плане? Она тут одинакова, я это показал на картинках со сравнением(+ можно в приложенных файлах посмотреть) разница только в способе выборке и итоговой цене

По стандартному индексу идет |--Index Seek(OBJECT:([MIS_PROD2].[dbo].[_InfoRg18860].[_InfoR18860_ByDims18897_STRRRR] AS [T1]), SEEK:([T1].[_Fld628]=[@P1]), WHERE:([MIS_PROD2].[dbo].[_InfoRg18860].[_Period] as [T1].[_Period]>=[@P2]) ORDERED FORWARD)
По нестандартному индексу идет скан с проверкой всех условий |--Index Scan(OBJECT:([MIS_PROD2].[dbo].[_InfoRg18860].[_InfoR18860_MySuperWithout_Fld628_ByDims18897_STRRRR] AS [T1]), WHERE:([MIS_PROD2].[dbo].[_InfoRg18860].[_Fld628] as [T1].[_Fld628]=[@P1] AND [MIS_PROD2].[dbo].[_InfoRg18860].[_Period] as [T1].[_Period]>=[@P2]) ORDERED FORWARD)

В целом мой посыл таков - раз мы не можем принуждать СУБД хинтами, то единственный вариант повысить эффективность использования индексов это Селективность + Оптимальная структура хранения (в том числе фрагментация), ведь после дефрагментации стандартный селективный индекс опять стал эффективным.

Более интересный вопрос с _Fld628 поле с низкой кардинальностью, стоит ли его ставить всегда в начало индекса как делает 1С? Напр в регистре бухгалтерии присуствует поле Организация, но за счет другой схемы построения стандартных индексов оно не мешает ;)
7. redfred 19.10.22 14:55 Сейчас в теме
(6)
Не знаю где Вы видите первичный ключ в нетрадиционном индексе,


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


(6)
Что подразумевается под селективностью предикатов? Rows в фактическом плане? Она тут одинакова, я это показал на картинках со сравнением(+ можно в приложенных файлах посмотреть) разница только в способе выборке и итоговой цене


Ну смотрите, например есть у нас колонка _Period, тип datetime, исторических данных много. Селективность более чем хорошая. И в сочетании с условным WHERE _Period = (SELECT GETDATE()), такой и остаётся, поскольку вернёт, скорее всего, от ноля до нескольких строк из нескольких миллионов. Но если в запросе будет WHERE _Period < (SELECT GETDATE()), то селективность данных не будет играть абсолютно никакой роли, потому что с этим предикатом будут выгребаться все строки. Нельзя глядя только на селективность данных сказать, что именно вот такой порядок колонок в индексе предпочтёт оптимизатор. Нужно смотреть на селективность конкретных запросов
8. sulfur17 60 01.06.23 10:10 Сейчас в теме
Буду рад видеть Вас на нашем телеграм-канале

а не подскажете ссылку на ваш ТГ-канал?)
9. 1CUnlimited 308 01.06.23 15:19 Сейчас в теме
(8) t.me/Chat1CUnlimited и в профиле тоже есть
Оставьте свое сообщение