При разработке высоконагруженных систем на платформе 1С:Предприятие 8 критически важно понимать, какие действия вызывают прямое обращение к системе управления базами данных (СУБД), а какие выполняются на уровне сервера приложений или в оперативной памяти клиента. Одним из частых вопросов, вызывающих споры среди разработчиков, является поведение метода ПолучитьСсылку() у менеджеров прикладных объектов. Обращается ли этот метод к базе данных? Как именно генерируются уникальные идентификаторы (UUID) и как оптимизировать поиск по ним в запросах? Разберем эти вопросы подробно и по шагам.
Для начала проанализируем, что представляет собой объект ссылки в платформе 1С. Ссылка — это чисто программная абстракция, структура в оперативной памяти сервера приложений или клиента. На физическом уровне она состоит всего из двух ключевых элементов:
Номенклатура или документу РеализацияТоваровУслуг) относится данный объект.Когда мы вызываем метод ПолучитьСсылку(), передавая в него конкретный УникальныйИдентификатор, платформа выполняет простую математическую и логическую операцию в памяти. Она склеивает идентификатор типа данных (который ей уже известен из метаданных конфигурации) и переданный UUID. При этом никакого обращения к СУБД не происходит.
Платформе абсолютно неважно, существует ли физически строка с таким идентификатором в таблице базы данных в данный момент. Ссылка будет успешно создана в памяти. Проверить это очень просто: мы можем передать в метод абсолютно случайный, сгенерированный "из головы" UUID, и платформа мгновенно вернет нам объект ссылки.
Рассмотрим пример кода:
МойУИД = Новый УникальныйИдентификатор("1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d");
// Метод выполняется мгновенно в памяти, СУБД не задействуется
СсылкаНаНоменклатуру = Справочники.Номенклатура.ПолучитьСсылку(МойУИД);
Запрос к СУБД будет отправлен только тогда, когда мы попытаемся "разыменовать" эту ссылку — то есть обратиться к ее реквизитам через точку (например, СсылкаНаНоменклатуру.Наименование) или вызвать метод ПолучитьОбъект(). В этот момент платформа сформирует SQL-запрос типа SELECT к таблице прикладного объекта с фильтром по полю _IDRRef = &Ссылка. Если объекта с таким UUID в базе нет, СУБД вернет пустой результат, а в интерфейсе 1С мы увидим надпись "<Объект не найден...>".
В ходе обсуждений разработчики иногда замечают, что при мониторинге активности в SQL Profiler (или технологическом журнале 1С) в момент вызова ПолучитьСсылку() фиксируются обращения к СУБД. Однако эти запросы адресуются не к таблицам прикладных объектов (справочников или документов), а к служебным таблицам базы данных (таким как Params, Config или v8users).
Выясним причину этого поведения. Это связано исключительно с состоянием кэша сервера приложений 1С («холодный» или «горячий» старт):
ПолучитьСсылку() в рамках этого сеанса (и соседних сеансов на этом же рабочем процессе) будут проходить без каких-либо обращений к СУБД.Таким образом, единичные запросы к служебным таблицам — это накладные расходы на инициализацию кэша метаданных платформы, а не следствие работы самого метода генерации ссылки.
Часто разработчики замечают, что последовательно созданные в одной сессии объекты имеют очень похожие UUID, отличающиеся буквально несколькими символами в начале, тогда как правая часть идентификатора остается полностью идентичной. Из-за этого возникает ложное предположение, что платформа делает запрос к СУБД к какому-то системному счетчику или автоинкременту, чтобы получить "следующий" свободный номер.
Проанализируем алгоритм генерации UUID. Платформа 1С:Предприятие использует международный стандарт RFC 4122 (версия 1 / Version 1, Time-Based). Этот алгоритм генерирует идентификаторы на основе двух ключевых параметров:
Именно поэтому UUID, созданные в рамках одного рабочего процесса на одном сервере, имеют одинаковую правую часть (MAC-адрес) и последовательно изменяющуюся левую часть (время). Для работы этого генератора СУБД абсолютно не нужна — вся информация берется из операционной системы сервера приложений.
Важное предупреждение о коллизиях в РИБ (распределенных базах):
Использование стандарта RFC 4122 v1 накладывает определенные риски при некорректном администрировании виртуальных машин. Если в компании развернута распределенная информационная база (РИБ), а серверы приложений в разных узлах были созданы путем «холодного» клонирования одной виртуальной машины (без изменения MAC-адреса сетевой карты), то их Node ID в генераторе UUID будут полностью совпадать. При точной синхронизации времени на этих серверах возникает риск генерации абсолютно идентичных UUID в разных узлах базы данных, что приведет к коллизиям при обмене данными. Чтобы избежать этого, всегда выполняйте процедуру sysprep и следите за уникальностью MAC-адресов сетевых плат на серверах.
Рассмотрим практическую задачу: у нас есть массив уникальных идентификаторов в виде строк или объектов УникальныйИдентификатор, и нам необходимо найти соответствующие им элементы справочника в запросе. Существует два основных подхода к решению этой задачи.
При этом подходе мы сначала преобразуем имеющиеся UUID в объекты ссылок в коде 1С, используя метод ПолучитьСсылку(), а затем передаем готовый массив ссылок в качестве параметра запроса.
Разберем этот вариант на примере кода:
// Исходный массив строковых UUID
МассивСтрокУИД = Новый Массив;
МассивСтрокУИД.Добавить("a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d");
МассивСтрокУИД.Добавить("f6e5d4c3-b2a1-0f9e-8d7c-6b5a4f3e2d1c");
// Шаг 1: Конвертируем строки в ссылки в оперативной памяти (без обращения к СУБД)
МассивСсылок = Новый Массив;
Для Каждого СтрокаУИД Из МассивСтрокУИД Цикл
ТекущийУИД = Новый УникальныйИдентификатор(СтрокаУИД);
СсылкаНаОбъект = Справочники.Номенклатура.ПолучитьСсылку(ТекущийУИД);
МассивСсылок.Добавить(СсылкаНаОбъект);
КонецЦикла;
// Шаг 2: Передаем готовые ссылки в запрос
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Наименование КАК Наименование
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка В (&МассивСсылок)";
Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);
РезультатЗапроса = Запрос.Выполнить();
Почему это максимально быстро с точки зрения СУБД?
Когда платформа транслирует этот запрос в SQL, массив ссылок преобразуется в массив 16-байтных двоичных значений BINARY(16). В SQL-запросе это превращается в конструкцию WHERE _IDRRef IN (...). Поскольку колонка _IDRRef является кластеризованным индексом (первичным ключом) таблицы прикладного объекта, СУБД выполняет операцию Index Seek (поиск по индексу). Это самый быстрый и наименее ресурсоемкий способ поиска в СУБД.
Начиная с версии платформы 8.3.22, в языке запросов 1С появилась встроенная функция УНИКАЛЬНЫЙИДЕНТИФИКАТОР(). Некоторые разработчики предпочитают передавать в запрос исходные строки или объекты UUID, выполняя сопоставление прямо в теле запроса.
Посмотрим на пример такого запроса:
// Передаем массив строк UUID напрямую в параметры запроса
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Наименование КАК Наименование
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| УНИКАЛЬНЫЙИДЕНТИФИКАТОР(Номенклатура.Ссылка) В (&МассивСтрокУИД)";
Запрос.УстановитьПараметр("МассивСтрокУИД", МассивСтрокУИД);
РезультатЗапроса = Запрос.Выполнить();
В чем опасность данного подхода для производительности?
Применение любых функций (в том числе функций преобразования типов или получения UUID) к индексируемым полям в условиях ГДЕ крайне негативно сказывается на работе оптимизатора запросов СУБД (MS SQL Server, PostgreSQL и др.).
Когда СУБД видит конструкцию WHERE Функция(_IDRRef) IN (...), она зачастую не может сопоставить это с индексом по полю _IDRRef. Вместо быстрого точечного поиска (Index Seek) СУБД будет вынуждена применить операцию Index Scan или даже Table Scan — то есть просканировать всю таблицу справочника целиком, вычисляя функцию для каждой строки базы данных. На больших таблицах (сотни тысяч или миллионы записей) такой запрос приведет к катастрофическому падению производительности и длительным блокировкам таблиц.
Подводя итог нашему анализу, зафиксируем основные правила эффективной работы с идентификаторами и ссылками в 1С:
ПолучитьСсылку() работает исключительно в оперативной памяти и никогда не инициирует запросов к таблицам данных СУБД для поиска или валидации существования объекта.ПолучитьСсылку() в коде 1С с последующей передачей массива ссылок в параметры запроса. Это гарантирует использование первичных индексов СУБД и мгновенное выполнение поисковых запросов.