Начнем издалека../
Что такое GUID или уникальный идентификатор объекта?
GUID (Globally Unique Identifier) - статический уникальный 128-битный идентификатор. Его главная особенность - уникальность, которая позволяет создавать расширяемые сервисы и приложения без опасения конфликтов, вызванных совпадением идентификаторов. Хотя уникальность каждого отдельного GUID не гарантируется, общее количество уникальных ключей настолько велико (2^128 или 3,4028*10^38), что вероятность того, что в мире будут независимо сгенерированы два совпадающих ключа, крайне мала.
GUID состоит из четырех частей разделенных символом тире и содержащих в сумме 32 символа, которые являются шестнадцатеричным представлением 16 байт (128 бит). Т.е. два символа в GUID (октет) – это один байт в шестнадцатеричном виде. Пример GUID: b0d4ce5d-2757-4699-948c-cfa72ba94f86
[1]
Почему используется guid, а не простой auto-increment(indentity)?
Для использования гуидов есть 2 причины:
- хотим чтобы база данных или какой-нибудь другой сервер(кластер) не имел централизованного управления генерацией ключей
- нужно избавиться от проблемы создания одинакового идентификатора на разных рабочих серверах
Такие проблемы обычно возникают в распределенной среде исполнения, а кластер серверов позиционируется как распределенный и желательно полностью избежать единой точки выдачи уникальных ключей.
Если ваша система не имеет таких требований, вам не нужен UUID.
[1] [2]
В чем разница между uuid и guid?
UUID это термин из стандарта rfc4122, который опубликует всемирная организация стандартизации eitf.
Грубо говоря GUID это то что получилось на практике у Microsoft. Теория различается с практикой так же, как и XML с HTML.
[1] [2]
В бд гуиды имеют тип binary(16) и сравниваются (сортируются) побитово как бинарные данные: binary bit-for-bit comparison behavior
upd: Как преобразовывать binary(16) в GUID1C и обратно?
Из чего состоит guid?
Определяется типом:
- Random: Just use the system’s random-number generator to create a 128-bit number.
- Time-based: Create a GUID based on the current time.
- Hardware-based: Make a GUID with certain portions based on hardware features, such as the MAC address of a network card.
- Content-based (MD5 or SHA-1 hash of data): Create a GUID based on a hash of the file contents. Files with the same contents will get the same GUID.
[1]
Как определить версию GUID?
Версия гуида определяется в старшем байте 7 октета.
b0:d4:ce:5d-27:57-46:99-94:8c-cf:a7:2b:a9:4f:86
4 - random, 1 - Time-based
[1]
Почему нельзя упорядочить по ссылке, если в ней содержится дата создания?
Как уже было описано, guid изначально был придуман для РАСПРЕДЕЛЕННЫХ систем, в которых ПРОБЛЕМА УНИКАЛЬНОСТИ идентификаторов решена полным ОТКАЗОМ ОТ АВТОИНКРЕМЕНТА в пользу СЛУЧАЙНЫХ чисел и специальных техник. GUIDы случайны и неповторяемы по определению и в этом его достоинство и недостаток. Например, в предопределенных элементах и произвольных идентификаторах используется Random GUIDs (Version 4). В "типизированных" же Time-Based GUIDs (Version 1).
Используются разные стандарты?
Да, но когда индексам БД приходится работать со случайными значениями ключей(см. B-Tree Insertion) возникают проблемы.
[1] [2]
То есть для ссылок может создаваться Random GUID?
Только для предопределенных элементов и вручную созданных гуидов.
ГУИДВидНоменклатуры = Новый УникальныйИдентификатор();
ВидНоменклатурыСсылка = Справочники.ВидыНоменклатуры.ПолучитьСсылку(ГУИДВидНоменклатуры);
ВидНоменклатурыОбъект = Справочники.ВидыНоменклатуры.СоздатьЭлемент();
ВидНоменклатурыОбъект.УстановитьСсылкуНового(ВидНоменклатурыСсылка);
ВидНоменклатурыОбъект.Наименование = "тест";
ВидНоменклатурыОбъект.Записать();
Результат:
Ссылка Код Предопределенный Наименование
4ad76228-517f-4496-aa6c-eef36d3de35a 000000001 истина Товар <-- Random UUID (version 3)
01d3ff0d-cc37-4863-adef-72d3b0dbf08a 000000002 истина Продукция
623bca27-dffd-4391-b233-20ca170549d4 000000010 ложь тест
769e3f33-50d7-11e7-bcda-94de807c3939 000000005 ложь 1 <-- Time-based UUID (version 1)
769e3f34-50d7-11e7-bcda-94de807c3939 000000006 ложь 2
769e3f35-50d7-11e7-bcda-94de807c3939 000000007 ложь 3
Первые три - random based, вторые три - time-based.
И все-таки?
Random UUIDs вызывают деградацию операций вставки. У таких гуидов индексы получаются плохо кластеризованы, дерево поиска максимально широкое. Для обхода этого недостатка был придуман COMB Guid - Combined Guid for the combination of a timestamp. Поэтому когда используюся Time-Based GUIDs для первичных ключей, они получаются более сгруппированными, но никак не последовательными. "Механизм генерации ссылок обеспечивает только их уникальность. Возрастающая последовательность при их генерации не обеспечивается." (c) БГ
[1]
Но у гуидов есть же последовательность? Что-то там про МоментВремени?
Да сколько можно? Нет у гуидов последовательности! Кто первый запросил - тому и выдается пул. А когда уж сеанс его исчерпает - зависит от него самого. Читаем подробнее про эксперимент с МоментомВремени.
//infostart.ru/public/84177/
Можно ли вытащить время из гуида?
Можно. Но не нужно.
bdb62d89-cede-11e4-b12b-d4ae52b5e909
Алгоритм:
дата содержится в первых символах, bdb62d89-cede-11e4 которые нужно переставить задом наперед: 11e4-cede-bdb62d89
первый символ отбрасываем, убираем "лишние" знаки "-"(тире)
интервал в десятых долях микросекунд (HEX) получается равным: интервал16= 1E4CEDEBDB62D89
переводим его в десяничный интервал интервал10 = HexToDec(интервал16);
в результате получаем: интервал10 = 136 461 344 788 852 105
находим интервал в секундах: интервалСек = интервал10 / 10 000 000;
Делаем сдвиг даты от 15.10.1582 г. + 13 646 134 478 + сдвиг на часовой пояс (Московское время) от "мирового времени" (GMT) = 20.03.2015 16:54:38
[1] [2] [3]
Почему части времени идут "задом-наперед"?
"Так сложилось" ;)
Например потому что guid'ы появились задолго до того, как до них добрались руки ietf и баз данных.
Или потому что платформа написана на C, а не на Java, а как мы знаем из асемблера архитектура x86 имеет little-endian byte order.
Или, как говорит википедия, использовалось 2 варианта: для передачи по сети "on-wire" "network" (big-endian) byte order, а для хранения "native" (little-endian) byte order.
В любом случая я не знаю как там было и можно только догадываться.
[1] [2] [3] [4]
А последние две части что значат?
bdb62d89-cede-11e4-b12b-d4ae52b5e909
Счетчик "уникальности" и физический MAC-адрес.
Почему нельзя использовать время из GUID?
Во-первых, гуид может быть случайным, а не основаным на времени.
Во-вторых, гуиды выдаются пулом по 32 штуки для каждого сеанса.
В-третьих, гуид случаен по своему стандарту и время в нем это лишь способ сгруппировать первичные ключи для уменьшения ширины В-дерева и ускорения операций вставки в кластерный индекс!
[1] [2] [3]
Если отчет из одной конфигурации копипастой тащу в другую - ID сохранится его?(в моей его не было) (c) 2michael
Ответ: При копировании объекта из одной конфы в другую _копипастом_ внутренний гуид меняется!
НО: при сравнении объединении этого не видно!, так как происходит сопоставление по имени.
НО: это не касается и предопределённых данных! Если добавлять их вручную, а потом конфу разработки сравнить-объединить с боевой - возникнут дубли в справочнике!
В edt же есть режим сравнения только по guid.
Именно поэтому создание нового объекта желательно делать через сравнение-объединение, иначе будет замедление на больших конфигурациях. Подробнее в ИТС: https://its.1c.ru/db/metod8dev/content/2299/hdoc
При замене отчета в дереве конфигурации командой "Заменить на внешнюю обработку, отчет..." меняется ли внутренний идентификатор объекта (отчета)? (с) Pandoch upd:02.07.20
Ответ: Нет, гуид при замене из файла остаётся прежним
Другая вариация вопроса: Есть две разные конфы. Но в них есть одинаковый объект метаданных например документ "Покупка". Как можно получить внутренний идентификатор этого объекта в обеих базах, используемый в сравнении и объединении, чтобы удостовериться, что этот объект, не зависимо от имени, замениться, а не дублируется ? Как я понял ЗначениеВСтрокуВнутр() дает не тот ID который нужен мне.
Вообще, внутренний идентификатор объекта программно узнать нельзя, а увидеть его можно в выгрузке конфигурации в файлы, строка <Report uuid="1ce0ffe7-2218-4d9d-88d5-d3eec204bfff">:
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" ... version="2.6">
<!--это внутренний гуид отчета:-->
<Report uuid="98b2e87d-e5cc-449a-aeab-e4ca51d5c46a">
<InternalInfo>
<!--это гуид типа объекта:-->
<xr:GeneratedType name="ReportObject.ДебиторскаяЗадолжность" category="Object">
<xr:TypeId>6737b9b2-732d-4051-8166-59d1d4209295</xr:TypeId>
<xr:ValueId>b93b9dff-ff02-4fd6-860b-ce98ef64df97</xr:ValueId>
</xr:GeneratedType>
<!--это гуид типа менеджера:-->
<xr:GeneratedType name="ReportManager.ДебиторскаяЗадолжность" category="Manager">
<xr:TypeId>b0291e09-dae8-47bb-b380-9c25be22fab3</xr:TypeId>
<xr:ValueId>4bfb0318-b989-43c3-937d-b32ac8b80385</xr:ValueId>
</xr:GeneratedType>
При каждой выгрузке во внешний отчет/обработку guid генерируется заного. При загрузке из файла - востанавливается. Это позволяет хоть 10 раз выгрузить отчет/обработу во внешний файл, и каждый из этих файлов можно будет открыть параллельно в клиенте.
ЗначениеВСтрокуВнутр() выдает идентификатор прикладного типа, а не внутреннего объекта метаданных. Помимо внутреннего идентификатора у каждого объекта метаданных есть идентификаторы типов. Например ОтчетМенеджер.<Имя отчета> и ОтчетОбъект.<ИмяОтчета>:
МенеджерОтчета = Отчеты.ДебиторскаяЗадолжность;
Сообщить(ТипЗнч(МенеджерОтчета)); // Отчет менеджер: Дебиторская задолжность
Сообщить(ЗначениеВСтрокуВнутр(МенеджерОтчета)); // {"#",b0291e09-dae8-47bb-b380-9c25be22fab3,
ОбъектОтчета = Отчеты.ДебиторскаяЗадолжность.Создать();
Сообщить(ТипЗнч(ОбъектОтчета)); // Отчет объект: Дебиторская задолжность
Сообщить(ЗначениеВСтрокуВнутр(ОбъектОтчета)); // {"#",6737b9b2-732d-4051-8166-59d1d4209295}
МетаданныеОтчета = Метаданные.Отчеты.ДебиторскаяЗадолжность;
Сообщить(ТипЗнч(МетаданныеОтчета)); // Объект метаданных
Сообщить(ЗначениеВСтрокуВнутр(МетаданныеОтчета)); // {"#",9fb58eea-17f2-4200-b105-b288d62f4303}
МетаданныеЯзыка = Метаданные.Языки.Русский;
Сообщить(ТипЗнч(МетаданныеЯзыка)); // Объект метаданных
Сообщить(ЗначениеВСтрокуВнутр(МетаданныеЯзыка)); // {"#",9fb58eea-17f2-4200-b105-b288d62f4303}
// выводит тоже самое, так как это гуид типа встроенного в платформу
Все типы имеют свои идентификаторы, но при загрузке через "Заменить на внешнюю обработку, отчет..." они, так же как и идентификатор метаданных, заменяются на текущие.
Например - загрузка в отчет ABCАнализПокупателей внешнего отчета ДебиторскаяЗадолжность.epf вызовет лишь добавление суффикса (такой отчет уже есть в конфигурации), а все идентификаторы остаются прежними.
Скрин до загрузки - после загрузки:
Идентификаторы не изменились.
Почему используется "перевернутый" формат UUID внутри 1с?
<Объект не найден> (26:80f408002771598b11e7a3f0a3a64c3b)
Не знаю. Знаю только что первая цифра соответствует имени таблицы в sql: Reference26 -> ВидыНоменклатуры
Есть же спецификация?
Есть.
bdb62d89-cede-11e4-b12b-d4ae52b5e909
Layout
0 1 2 3
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| time_low |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| time_mid | time_hi_and_version |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|clk_seq_hi_res | clk_seq_low | node (0-1) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| node (2-5) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Bit mask
FFFFFFFF-0000-0000-0000-000000000000 time_low
00000000-FFFF-0000-0000-000000000000 time_mid
00000000-0000-F000-0000-000000000000 version
00000000-0000-0FFF-0000-000000000000 time_hi
00000000-0000-0000-C000-000000000000 variant
00000000-0000-0000-3FFF-000000000000 clock_seq
00000000-0000-0000-0000-FFFFFFFFFFFF node
Byte order
Field Data Type Octet Note
#
time_low unsigned 32 0-3 The low field of the
bit integer timestamp
time_mid unsigned 16 4-5 The middle field of the
bit integer timestamp
time_hi_and_version unsigned 16 6-7 The high field of the
bit integer timestamp multiplexed
with the version number
clock_seq_hi_and_rese unsigned 8 8 The high field of the
rved bit integer clock sequence
multiplexed with the
variant
clock_seq_low unsigned 8 9 The low field of the
bit integer clock sequence
node unsigned 48 10-15 The spatially unique
bit integer node identifier
Расшифровываю:
Timestamp - это 60-битное число, содержащее количество 100-наносекундных интервалов с 15 октября 1582 г.
Часть low обнуляется каждые 2^32 / 10^7 / 60 ~ 7 минут, часть mid через 1 год, часть hi сами представляете.
Version - старшие 4 бита в седьмом октете, содержат тип гуида.
0x0001 1 time-based version
0x0010 2 DCE Security version (POSIX UIDs)
0x0011 3 name-based version (MD5 hashing)
0x0100 4 randomly generated version
0x0101 5 name-based version (SHA-1 hashing)
Clock Sequence - используется чтобы избежать появления дубликатов, когда часы переводятся назад или меняется идентификатор узла. Если предыдущее значение счетчика известно - то увеличивается на единицу, иначе берется случайное число.
Node - содержит физический MAC-адрес сервера. Дада, проверьте ipconfig /all ;)
Примеры? Есть их у меня.
Мы же "программисты", накодим функции:
// Возвращает время создания GUID'а
//
Функция UUID_Timestamp(ГУИД)
// Например ГУИД равен: bdb62d89-cede-11e4-b12b-d4ae52b5e909
// Дата содержится в первых символах, bdb62d89-cede-11e4 которые нужно переставить задом наперед: 11e4-cede-bdb62d89
Строка16 = Сред(ГУИД, 15, 4) + Сред(ГУИД, 10, 4) + Сред(ГУИД, 1, 8);
// Убираем "лишние" знаки "-"(тире).
Строка16 = СтрЗаменить(Строка16, "-", "");
// Убираем первый символ, так как в нем содержится версия стандарта (зашит в седьмой октет)
Строка15 = Сред(Строка16, 2);
// Получаем timestamp в 60 бит : 1E4 CEDE BDB6 2D89
ЧислоСек = 0;
Для Позиция = 1 По СтрДлина(Строка15) Цикл
ЧислоСек = ЧислоСек + Найти("123456789abcdef",Сред(Строка15,Позиция,1))*Pow(16,СтрДлина(Строка15) - Позиция);
КонецЦикла;
ЧислоСек = ЧислоСек / 10000000;
// Прибавляем к дате начала Григореанского календаря
Возврат Дата(1582, 10, 15, 00, 00, 00) + ЧислоСек + СмещениеСтандартногоВремени() + СмещениеЛетнегоВремени();;
КонецФункции
// Возвращает версию GUID'а
//
Функция UUID_Version(ГУИД)
// Пусть ГУИД равен: bdb62d89-cede-11e4-b12b-d4ae52b5e909
// Тогда номер версии содержится в старшем байте седьмого октета
Версия = Сред(ГУИД, 15, 1)*1;
СтрокаВерсий = "Version 1. Time-based
| Version 2. DCE Security (POSIX)
| Version 3. Name-based (MD5 hashing)
| Version 4. Random
| Version 5. Name-based (SHA-1 hashing)
| Unknown version";
Возврат СтрПолучитьСтроку(СтрокаВерсий, Мин(СтрЧислоСтрок(СтрокаВерсий), Версия));
КонецФункции
// Возвращает mac-адрес сервера
//
Функция UUID_Node(ГУИД)
// Пусть ГУИД равен: bdb62d89-cede-11e4-b12b-d4ae52b5e909
// Тогда MAC-адрес в последней части
Строка12 = Сред(ГУИД, 25);
// Разделим на октеты
Результат = "";
Для Позиция = 1 По СтрДлина(Строка12) Цикл
Если Позиция % 2 = 1 Тогда
Результат = Результат + " ";
КонецЕсли;
Результат = Результат + ВРЕГ(Сред(Строка12,Позиция,1));
КонецЦикла;
Результат = СтрЗаменить(СокрЛП(Результат), " ", ":");
Возврат Результат;
КонецФункции
Проверим ссылку обычного документа:
// Вытащим информацию из ссылки, созданной системой
Выборка = Документы.ПоступлениеТоваровУслуг.Выбрать( , , , "Дата УБЫВ");
Если Выборка.Следующий() Тогда
ГУИД = Выборка.Ссылка.УникальныйИдентификатор();
Сообщить("Ссылка: " + Выборка.Ссылка);
Сообщить("GUID: " + ГУИД);
Сообщить("Дата создания: " + UUID_Timestamp(ГУИД));
Сообщить("Версия: " + UUID_Version(ГУИД));
Сообщить("MAC-адрес хоста: " + UUID_Node(ГУИД));
КонецЕсли;
// Получим:
//Ссылка: Поступление товаров и услуг ВА000000560 от 06.06.2017 0:00:01
//GUID: a7de1f17-4a83-11e7-9e20-0021918e8333
//Дата создания: 06.06.2017 10:44:57
//Версия: Version 1. Time-based
//MAC-адрес хоста: 00:21:91:8E:83:33
Проверим ссылку, сформированную вручную:
// Вытащим информацию из ссылки, созданной &НаКлиенте
ДокСсылка = Документы.ПоступлениеТоваровУслуг.ПолучитьСсылку(Новый УникальныйИдентификатор());
ГУИД2= ДокСсылка.УникальныйИдентификатор();
Сообщить("Ссылка нового: " + ДокСсылка);
Сообщить("GUID нового: " + ГУИД2);
Сообщить("Версия нового: "+ UUID_Version(ГУИД2));
//Ссылка нового: <Объект не найден> (152:936654ac78639f43411ad7a77db94d48)
//GUID нового: 7db94d48-d7a7-411a-9366-54ac78639f43
//Версия нового: Version 4. Random
Проверим работу счетчика "уникальности":
// Проверим работу clock_seq
Для й=1 по 32 Цикл
НачатьТранзакцию();
ВидНоменклатураОбъект = Справочники.НоменклатураТест.СоздатьЭлемент();
ВидНоменклатураОбъект.наименование = "тест1";
ВидНоменклатураОбъект.Записать();
ГУИД = ВидНоменклатураОбъект.Ссылка.УникальныйИдентификатор();
Сообщить(ГУИД);
ОтменитьТранзакцию();
КонецЦикла;
// переведем время на минуту назад
КомандаСистемы("time "+Формат(ТекущаяДата()-60, "ДФ='hh:mm:ss tt'"));
// Выберем весь старый пул
Для й=1 по 32 Цикл
НачатьТранзакцию();
ВидНоменклатураОбъект = Справочники.НоменклатураТест.СоздатьЭлемент();
ВидНоменклатураОбъект.наименование = "тест2";
ВидНоменклатураОбъект.Записать();
ГУИД = ВидНоменклатураОбъект.Ссылка.УникальныйИдентификатор();
Сообщить(ГУИД);
ОтменитьТранзакцию();
КонецЦикла;
// Получим:
//...
//81c42878-526c-11e7-80f0-08002771598b
//81c42879-526c-11e7-80f0-08002771598b
//81c4287a-526c-11e7-80f0-08002771598b
//723dee43-526c-11e7-80f1-08002771598b
//723dee44-526c-11e7-80f1-08002771598b
//...
Можно даже так:
НачатьТранзакцию();
ГУИД = Новый УникальныйИдентификатор("deadbeef-d7a7-411a-9366-defaced0babe");
НовыйОбъект = Справочники.Номенклатура.СоздатьЭлемент();
НовыйОбъект.УстановитьСсылкуНового(Справочники.Номенклатура.ПолучитьСсылку(ГУИД));
НовыйОбъект.ВидНоменклатуры = Справочники.ВидыНоменклатуры.ПолучитьСсылку(ГУИД);
НовыйОбъект.Наименование = "Свежее мясо";
НовыйОбъект.ОбменДанными.Загрузка = Истина;
НовыйОбъект.Записать();
Сообщить("GUID нового: " + НовыйОбъект.Ссылка.УникальныйИдентификатор());
Сообщить("Реквизит нового: " + НовыйОбъект.ВидНоменклатуры);
ОтменитьТранзакцию();
//GUID нового: deadbeef-d7a7-411a-9366-defaced0babe
//Реквизит нового: <Объект не найден> (5433:9366defaced0babe411ad7a7deadbeef)
Какой мак-адрес у меня, вы уже знаете ;)
ps: под "упорядочить по ссылке" везде имеется ввиду сортировка в порядке создания ссылок и вообще в каком-либо порядке, отличном от сравнения в побитовом бинарном формате хранения бд.
Ну вот и все.
Надеюсь, теперь мысль о том, чтобы "упорядочить по ссылке", я из вас вытряхнул окончательно.
;-P