При проектировании высоконагруженных систем на платформе 1С разработчики часто сталкиваются с дилеммой: использовать стандартные механизмы (ссылочные типы) или прибегать к денормализации данных (дублирование кодов, артикулов в виде примитивных типов) ради призрачного или реального ускорения запросов. Рассмотрим ситуацию, когда в регистре сведений РегистрСведений.Остатки у нас есть измерение Товар (ссылка) и реквизит КодТовара (число). Выясним, какой отбор отработает быстрее и почему.
Для того чтобы сделать правильный выбор, нам необходимо заглянуть «под капот» системы и проанализировать, как 1С транслирует наши объекты в таблицы базы данных SQL. Проанализируем два ключевых типа индексов:
Товар является первым измерением, поиск по нему будет максимально эффективным (Index Seek).КодТовара, система создает дополнительный индекс. Важно понимать, что в 1С индекс по реквизиту — это не просто колонка кода. Это структура вида Реквизит + Все Измерения.Разберем, как ведут себя типы данных. Ссылка в 1С (СправочникСсылка) хранится как binary(16) (128-битный UUID). Число длиной 6 хранится как numeric(6,0). С точки зрения процессора SQL-сервера, сравнение этих величин происходит практически мгновенно. Миф о том, что «числа всегда быстрее ссылок», в современных СУБД практически не имеет под собой оснований, если речь идет о точечной выборке по индексу.
Рассмотрим ситуацию, когда Товар стоит первым в списке измерений регистра. В этом случае запрос будет выглядеть следующим образом:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Остатки.Склад,
| Остатки.Количество
|ИЗ
| РегистрСведений.Остатки КАК Остатки
|ГДЕ
| Остатки.Товар = &Товар";
Запрос.УстановитьПараметр("Товар", ВыбранныйТовар);
Результат = Запрос.Выполнить();
В данном сценарии SQL-сервер использует кластерный индекс. Это самый быстрый способ получения данных, так как сам индекс и есть таблица. Нам не нужно делать дополнительных переходов (Key Lookup) за другими полями (например, количеством или ценой).
Теперь проанализируем ситуацию, когда мы решили искать по числовому коду, который хранится в реквизитах:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Остатки.Склад,
| Остатки.Количество
|ИЗ
| РегистрСведений.Остатки КАК Остатки
|ГДЕ
| Остатки.КодТовара = &КодТовара";
Запрос.УстановитьПараметр("КодТовара", 123456);
Результат = Запрос.Выполнить();
Здесь ситуация сложнее. SQL-сервер сначала найдет запись в некластерном индексе по полю КодТовара. Но так как в этом индексе нет данных о ресурсе Количество, серверу придется выполнить операцию Key Lookup — обратиться к основной таблице (кластерному индексу) по значениям измерений, которые неявно входят в индекс реквизита. Это создает дополнительную нагрузку на дисковую подсистему и память.
Существует исключение, когда поиск по реквизиту может сравняться или даже обойти поиск по ссылке. Это происходит при использовании так называемых покрывающих индексов. Если ваш запрос выбирает только те поля, которые уже входят в индекс реквизита (а это сам реквизит и все измерения), то SQL-сервер не полезет в основную таблицу. Это называется Index Only Scan.
Однако в задачах получения остатков (где нужно получить ресурс Количество), реквизит никогда не станет покрывающим, так как ресурсы не входят в индекс реквизита. Следовательно, поиск по измерению-ссылке всегда будет выигрышнее или как минимум не медленнее.
Проанализировав доводы, можно составить пошаговый алгоритм оптимизации регистра:
Товар, поставьте его выше Склада. Это обеспечит максимальную скорость поиска по ссылке без создания лишних индексов.LEFT JOIN по первичному ключу справочника. Выигрыш от отсутствия соединения часто нивелируется раздуванием размера базы и сложностью поддержки актуальности этого кода (при изменении кода в справочнике придется обновлять весь регистр).ГДЕ Ссылка В (&Список) при очень большом списке ссылок может работать медленнее, чем поиск по конкретному значению, независимо от типа данных.Резюме: В 99% случаев в 1С следует отдавать предпочтение отбору по первому измерению-ссылке. Использование числового реквизита оправдано только в специфических сценариях интеграции с внешними системами, которые не умеют работать с GUID, или если изменить порядок измерений в существующем нагруженном регистре технически невозможно.