При разработке прикладных решений часто возникает задача формирования короткого, удобного для восприятия пользователем номера (например, 8–9 знаков) вместо длинного и сложного строкового GUID (УникальныйИдентификатор). Если данные сохраняются в регистр сведений несколькими пользователями одновременно, простая выборка максимального значения запросом может привести к возникновению дублей и конфликтам блокировок. Разберем основные архитектурные варианты и практические способы безопасного формирования уникальных номеров в 1С.
Прежде чем приступать к программной генерации номеров, проанализируем структуру хранения данных. В платформе 1С сущности разделены по функциональному назначению:
Документ. Документы платформы уже имеют встроенный оптимизированный механизм автонумерации, контроль уникальности и поддержку транзакционной записи.Справочник, где также реализована встроенная автонумерация кодов.РегистрСведений. В этом случае измерением выступает ссылка на документ (или справочник), а ресурсом — значение статуса. При такой схеме генерировать искусственный уникальный номер для каждой записи регистра не требуется, так как уникальность обеспечивается комбинацией измерений и периода.Если специфика интеграции или бизнес-логики все же требует иметь независимый регистр сведений с уникальным числовым номером записи, рассмотрим надежные технические подходы к его формированию.
Самый простой в реализации способ, позволяющий переложить задачу поддержания уникальности и параллельного инкремента на механизмы платформы, — создание вспомогательного справочника (например, СлужебныеНомера).
Разберем по шагам логику работы этого метода:
Число с автонумерацией и контролем уникальности.Посмотрим на пример программной генерации номера через вспомогательный справочник:
Функция ПолучитьНовыйНомерЧерезСправочник()
НовыйЭлемент = Справочники.СлужебныеНомера.СоздатьЭлемент();
НовыйЭлемент.Записать();
Возврат НовыйЭлемент.Код;
КонецФункции
Если не требуется плодить ссылочные элементы в базе данных, реализуем классический паттерн счетчика на независимом регистре сведений с явной управляемой блокировкой.
Выясним причину возможных проблем при обычном получении максимума: если два сеанса одновременно прочитают максимальный номер, они получат одинаковое число и попытаются записать одну и ту же запись. Чтобы этого избежать, используем объект БлокировкаДанных в режиме РежимБлокировкиДанных.Исключительный строго по имени счетчика.
Разберем структуру регистра Счетчики:
ИмяСчетчика (Тип: Строка, длина 50).Значение (Тип: Число, длина 10, точность 0).Рассмотрим подробнее программный код получения очередного номера в короткой транзакции:
Функция ПолучитьСледующийНомер(ИмяСчетчика)
НачатьТранзакцию();
Попытка
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.Счетчики");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.УстановитьЗначение("ИмяСчетчика", ИмяСчетчика);
Блокировка.Заблокировать();
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Счетчики.Значение КАК Значение
|ИЗ
| РегистрСведений.Счетчики КАК Счетчики
|ГДЕ
| Счетчики.ИмяСчетчика = &ИмяСчетчика";
Запрос.УстановитьПараметр("ИмяСчетчика", ИмяСчетчика);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
ТекущееЗначение = Выборка.Значение;
Иначе
ТекущееЗначение = 0;
КонецЕсли;
СледующийНомер = ТекущееЗначение + 1;
МенеджерЗаписи = РегистрыСведений.Счетчики.СоздатьМенеджерЗаписи();
МенеджерЗаписи.ИмяСчетчика = ИмяСчетчика;
МенеджерЗаписи.Значение = СледующийНомер;
МенеджерЗаписи.Записать(Истина);
ЗафиксироватьТранзакцию();
Возврат СледующийНомер;
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки;
КонецФункции
Важный момент: Управляемая блокировка накладывается исключительно на одну строку в служебном регистре Счетчики. Целевой регистр со статусами не блокируется, а параллельные фоновые задания могут свободно читать из него данные без ожидания освобождения блокировок (при использовании версионирования строк на уровне СУБД).
В высоконагруженных системах, где частота записи высока, последовательное обращение к единому счетчику может вызывать очередь ожиданий. В Библиотеке Стандартных Подсистем (БСП) реализован механизм пакетного выделения идентификаторов (функция РаботаСФайламиСлужебный.ПолучитьОчереднойИдентификаторОбъекта()).
Принцип пакетного резервирования строится следующим образом:
Ограничение подхода: Если сеанс завершится аварийно, неиспользованные номера из локального пула будут пропущены. Это допустимо для технических и пользовательских идентификаторов, но не подходит для строгой сплошной бухгалтерской нумерации.
Попытка получить 8–9-значный номер путем обрезания или хеширования УникальныйИдентификатор (например, взятие остатка от деления или первых разрядов хеша) не гарантирует уникальность. Вследствие «парадокса дней рождения» в пространстве из 9 знаков (1 000 000 000 значений) вероятность коллизии (совпадения номеров) резко возрастает уже после генерации первых десятков тысяч записей, что приведет к перезаписи существующих данных или ошибкам уникальности.