В процессе разработки и поддержки систем на платформе 1С специалисты часто сталкиваются с понятием GUID (Globally Unique Identifier), или Уникального Идентификатора ссылочных объектов. На первый взгляд кажется, что это абсолютно случайная строка из 36 символов, исключающая любые совпадения. Однако на практике возникают ситуации, когда объекты в разных информационных базах получают идентичные идентификаторы и перезаписывают друг друга при обмене.
Разберем подробно, на основе каких данных платформа 1С генерирует GUID, почему строковое представление отличается от бинарного хранения в СУБД, по каким причинам происходят коллизии и как разработчик может управляемо создавать идентификаторы.
Вопреки распространенному заблуждению, платформа 1С формирует GUID не по стандарту случайных чисел (типа UUID v4), а использует модифицированный последовательный алгоритм — SequentialUUID.
Проанализируем, как происходит генерация ссылки нового объекта в рамках пользовательского или сервисного сеанса:
UuidCreateSequential или Linux-функция uuid_generate_time). Этот базовый GUID опирается на текущую метку времени, системный счетчик последовательности RPC и аппаратно-системные идентификаторы оборудования.Посмотрим на пример того, как выглядят последовательно созданные GUID в одном сеансе 1С:
Стартовый GUID: 6F9619FF-8B86-D011-B42D-00CF4FC964F0
Следующий объект: 6F9619FF-8B86-D011-B42D-00CF4FC964F1
Следующий объект: 6F9619FF-8B86-D011-B42D-00CF4FC964F2
Такой механизм обеспечивает высокую скорость создания объектов и оптимальную упаковку данных в индексах СУБД.
Если сравнить строковое представление уникального идентификатора в коде 1С и его фактическое бинарное значение в таблицах СУБД (например, в MS SQL Server в поле типа binary(16)), можно заметить различие в порядке следования групп символов.
Рассмотрим, как платформа 1С выполняет байтовую перестановку при записи на уровень СУБД:
Строковое представление УникальныйИдентификатор в 1С:
6F9619FF-8B86-D011-B42D-00CF4FC964FF
Фактическое хранение в физической таблице СУБД:
6F9619FF-D011-8B86-B42D-00CF4FC964FF
Зачем нужна байтовая перестановка?
В СУБД MS SQL Server или PostgreSQL первичный ключ по ссылочному полю часто входит в кластерный индекс. Если бы последовательно возрастающая часть GUID находилась в самом конце бинарного значения при стандартной сортировке SQL, каждая новая запись приводила бы к случайной вставке в произвольные страницы индекса, что вызвало бы сильную фрагментацию физических файлов базы данных.
Платформа 1С меняет байты местами таким образом, чтобы инкрементируемая (часто меняющаяся) часть перемещалась в нужную позицию. В результате последовательно созданные объекты записываются в физические таблицы СУБД монотонно, предотвращая фрагментацию индексов и существенно повышая производительность операций записи.
Теоретическая вероятность случайного совпадения двух 128-битных UUID крайне мала. Однако на практике коллизии (появление дублей GUID) встречаются довольно часто. Выясним основные причины их возникновения:
Sysprep) и на этих машинах одновременно запускаются сеансы 1С, системный API генерации GUID может вернуть абсолютно одинаковые стартовые значения.При использовании распределенных информационных баз (РИБ) или обменов данными коллизия GUID приводит к тому, что документ или элемент справочника, созданный в одном филиале, при загрузке обмена заменяет собой совершенно другой документ, созданный в другом филиале.
В платформах 1С 8.x разработчик имеет возможность переопределить стандартный GUID до момента первичной записи объекта в базу данных. Для этого используются методы УстановитьСсылкуНового() у объекта или метод ПолучитьСсылку() у менеджера соответствующего объекта.
Рассмотрим пример генерации детерминированного GUID на основе уникального бизнес-ключа (например, при интеграции со сторонней системой):
// Пример формирования GUID на основе хэша внешнего кода
ВнешнийИдентификатор = "VENDOR_CODE_99481";
// Получаем MD5-хэш от строки (32 шестнадцатеричных символа)
Хеш = ЗашифроватьСтроку(ВнешнийИдентификатор, СпособХеширования.MD5);
// Формируем канонический формат GUID: 8-4-4-4-12
СтрGUID = Сред(Хеш, 1, 8) + "-" +
Сред(Хеш, 9, 4) + "-" +
Сред(Хеш, 13, 4) + "-" +
Сред(Хеш, 17, 4) + "-" +
Сред(Хеш, 21, 12);
НовыйGUID = Новый УникальныйИдентификатор(СтрGUID);
// Привязываем сформированный GUID к новому элементу справочника
НовыйЭлемент = Справочники.Контрагенты.СоздатьЭлемент();
НоваяСсылка = Справочники.Контрагенты.ПолучитьСсылку(НовыйGUID);
НовыйЭлемент.УстановитьСсылкуНового(НоваяСсылка);
НовыйЭлемент.Наименование = "Тестовый Контрагент";
НовыйЭлемент.Записать();
Проанализируем важные правила работы с GUID при программной разработке:
УстановитьСсылкуНового() работает только до вызова метода Записать(). После того как объект записан в базу данных, изменить его GUID штатными средствами 1С невозможно.Sysprep при клонировании образцов ОС и контролируйте уникальность MAC-адресов виртуальных сетевых адаптеров.