Влияет ли порядок измерений в регистре накопления на производительность в 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Среди разработчиков 1С долгое время ведется дискуссия о том, имеет ли значение порядок следования измерений при проектировании регистров накопления и сведений. Некоторые считают это пережитком «эпохи 1С:Предприятия 7.7», другие же утверждают, что неверное расположение полей способно замедлить работу системы в разы. Давайте детально разберем эту тему, проанализируем поведение современных систем управления базами данных (СУБД), таких как MS SQL Server и PostgreSQL, и выясним, как порядок измерений влияет на производительность запросов и скорость записи.

Основы индексации: как СУБД работает с составными индексами

Для понимания сути проблемы нам необходимо обратиться к архитектуре баз данных. Главное правило разработчика БД — попасть в индекс. Платформа 1С:Предприятие автоматически создает индексы для таблиц СУБД на основе структуры метаданных, заданной в конфигураторе. В случае регистров накопления наиболее критичным является кластерный индекс таблицы итогов.

Кластерный индекс физически определяет порядок хранения строк в таблице на диске. Для таблицы итогов регистра остатков платформа формирует этот индекс по следующей структуре:

Период + Измерение_1 + Измерение_2 + ... + Измерение_N

Составной индекс работает по правилу левого префикса. Это означает, что СУБД может использовать этот индекс для быстрого поиска (операция Index Seek) только в том случае, если в условиях запроса («ГДЕ» или параметры виртуальной таблицы) указаны первые поля индекса. Если мы пропускаем первое поле составного индекса, но накладываем условие на второе или третье, СУБД не сможет выполнить эффективный поиск по дереву индекса и будет вынуждена прибегнуть к сканированию всей таблицы (операция Index Scan), что крайне негативно сказывается на производительности.

Развенчание мифа о чистой селективности полей

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

Если в запросе накладываются условия равенства на все поля составного индекса, СУБД абсолютно все равно, в каком порядке эти поля расположены в индексе. Скорость поиска будет одинаковой, поскольку высота B-Tree дерева и количество считываемых страниц данных не изменятся. Например, если в запросе жестко ограничены и Организация, и Склад, и Номенклатура, порядок их объявления в метаданных регистра не повлияет на скорость выполнения конкретно этого запроса.

Порядок полей важен не из-за самой селективности, а из-за частоты совместного или раздельного использования полей в реальных запросах системы. И именно здесь кроется причина, почему в типовых конфигурациях 1С первым измерением всегда идет наименее селективное поле — Организация.

Почему «Организация» всегда должна быть первой?

В любой типовой конфигурации 1С (ERP, КА, УТ, БП) в регистрах остатков первым измерением практически всегда выступает Организация, затем идет Склад, и только потом Номенклатура. Это кажется нелогичным с точки зрения селективности (организаций в базе обычно мало, а номенклатуры — сотни тысяч), однако этот подход является максимально оптимальным по двум причинам:

  1. Сквозная фильтрация во всех запросах. Почти 100% запросов в системе (отчеты, проведение документов, заполнение форм) содержат отбор по конкретной организации. Более того, механизмы разграничения прав на уровне записей (RLS) неявно добавляют условие по организации практически во все выборки. Поскольку это поле присутствует в запросах всегда, оно идеально выполняет роль «левого префикса» индекса. Если бы первой шла Номенклатура, то при запросе остатков по организации в целом СУБД не смогла бы использовать индекс и уходила бы в тяжелый Index Scan.
  2. Эффективное сжатие страниц (Page Compression) в СУБД. Данные в таблицах итогов хранятся на диске в физическом порядке кластерного индекса. Когда на первом месте стоит низкоселективное поле (одно и то же значение организации дублируется в миллионах строк подряд), алгоритмы сжатия данных в MS SQL Server или PostgreSQL упаковывают эти строки на диске и в оперативной памяти (Buffer Pool) максимально эффективно. Это значительно экономит ресурсы сервера.

Игнорирование индексов оптимизатором СУБД

Давайте рассмотрим ситуацию, когда разработчик решает проиндексировать отдельное измерение регистра, установив у него свойство «Индексировать» в конфигураторе. Например, у нас есть регистр с измерениями: Номенклатура (1-е измерение), Характеристика (2-е), Склад (3-е, индексированное).

Если мы пишем запрос на получение остатков только по конкретному складу, мы ожидаем, что СУБД использует созданный индекс по измерению Склад. Однако на практике оптимизатор запросов СУБД часто игнорирует этот индекс. Разберем по шагам, почему так происходит:

  1. Дополнительный индекс по полю Склад не содержит данных о самих остатках (ресурсах регистра). Он содержит только значение склада и ссылку на кластерный индекс.
  2. Чтобы выдать результат (остатки товаров), СУБД сначала должна найти строки в индексе по складу, а затем для каждой строки обратиться к кластерному индексу таблицы итогов за числовыми значениями ресурсов.
  3. Оптимизатор СУБД оценивает стоимость этих двух операций. Если выборка по складу затрагивает существенную часть таблицы, СУБД примет решение полностью проигнорировать индекс по складу и сразу выполнить сканирование кластерного индекса всей таблицы (Clustered Index Scan).

Таким образом, наличие индекса не гарантирует его использование, если структура основного кластерного индекса (порядок измерений регистра) спроектирована неоптимально.

Влияние ширины индекса на запись данных

При добавлении новых измерений в регистр важно помнить о «ширине» кластерного ключа. Кластерный индекс физически удерживает структуру всей таблицы. Все дополнительные некластерные индексы (создаваемые платформой при индексировании измерений) хранят внутри себя ключи кластерного индекса в качестве указателей на строки.

Если у вас в регистре создано слишком много измерений (например, 8-10 детальных аналитик), то:

Технология Skip Scan в современных СУБД

В современных СУБД (например, в PostgreSQL начиная с версии 15) реализована оптимизация Index Skip Scan (сканирование с пропуском). Она позволяет использовать составной индекс, даже если первое поле индекса пропущено в условиях запроса. Например, когда мы ищем остатки конкретного товара без указания организации.

Однако этот механизм работает эффективно только тогда, когда пропущенное первое поле имеет очень мало уникальных значений (низкую кардинальность), как в случае с организацией. Если бы на первом месте стояла высокоселективная номенклатура (миллионы уникальных записей), технология Skip Scan физически не смогла бы отработать оптимально, и СУБД гарантированно ушла бы в полное сканирование таблицы.

Пример влияния порядка измерений на практике

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

Вариант 1 (Неоптимальный):
Порядок измерений: Номенклатура -> Склад.

При попытке получить остатки по конкретному складу СУБД не сможет использовать левый префикс кластерного индекса таблицы итогов (так как первым полем идет номенклатура, которая в запросе не указана). Запрос вида:


ВЫБРАТЬ
    ТоварыОстатки.Номенклатура,
    ТоварыОстатки.Склад,
    ТоварыОстатки.ВНаличииОстаток
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, Склад = &Склад) КАК ТоварыОстатки

приведет к полному сканированию таблицы итогов (Index Scan), что при миллионной базе вызовет колоссальные задержки.

Вариант 2 (Оптимальный):
Порядок измерений: Склад -> Номенклатура.

В данном случае СУБД мгновенно выполнит операцию точечного поиска (Index Seek) по первому измерению Склад, ограничив выборку только нужными записями. Производительность такого запроса будет максимальной.

Итоговые рекомендации по проектированию регистров

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

  1. На первое место всегда ставьте измерения, которые гарантированно будут использоваться практически во всех запросах к этому регистру (например, Организация, НаправлениеДеятельности).
  2. Если в запросах часто используется отбор по конкретному измерению без указания остальных аналитик, это измерение должно располагаться как можно ближе к началу списка (быть левым префиксом).
  3. Старайтесь не плодить избыточные измерения. Если аналитику можно вынести в свойства номенклатуры (через реквизиты или сведения) или хранить в подчиненном справочнике, лучше сделать это, чтобы не раздувать ширину кластерного индекса.
  4. Установка флага «Индексировать» на измерениях помогает только для простых выборок, но не спасает при неоптимальной структуре самого регистра. Основной упор всегда делайте на правильное распределение порядка измерений сверху вниз.
← На главную