Последовательные GUID

Я надеюсь, что кто-то может ответить на этот вопрос.

Как метод UuidCreateSequential в классе rpcrt4.dll используется для заполнения его направляющих?

Я много знаю об этом: Microsoft изменила функцию UuidCreate, чтобы она больше не использовала MAC-адрес компьютера как часть UUID. Поскольку CoCreateGuid вызывает UuidCreate для получения GUID, его выходные данные также изменились. Если вы по-прежнему хотите, чтобы GUID генерировались в последовательном порядке (полезно для сохранения связанной группы GUID вместе в системном реестре), вы можете использовать функцию UuidCreateSequential.

Причиной вопроса является. Если я использую эту функцию для генерации последовательных идентификаторов GUID в веб-кластере, как я могу гарантировать, что идентификаторы GUID близки к диапазону идентификаторов GUID без возможности дублирования идентификатора GUID?

5 ответов

Решение

Win32 UuidCreateSequential создает Version 1UUID

Вот пример UUID версии 1, созданный на моем компьютере с помощью UuidCreateSequential:

{1BE8D85D-63D1-11E1-80DB-B8AC6FBE26E1}
{1BE8D85E-63D1-11E1-80DB-B8AC6FBE26E1}
{1BE8D85F-63D1-11E1-80DB-B8AC6FBE26E1}
{1BE8D860-63D1-11E1-80DB-B8AC6FBE26E1}
{1BE8D861-63D1-11E1-80DB-B8AC6FBE26E1}
{1BE8D862-63D1-11E1-80DB-B8AC6FBE26E1}
{1BE8D863-63D1-11E1-80DB-B8AC6FBE26E1}
{1BE8D864-63D1-11E1-80DB-B8AC6FBE26E1}
{1BE8D865-63D1-11E1-80DB-B8AC6FBE26E1}
{220FB46C-63D1-11E1-80DB-B8AC6FBE26E1}

Первое, что важно отметить, что эти uuid содержат MAC-адрес моей машины (B8AC6FBE26E1):

                        Node
======================= ============
1BE8D85D-63D1-11E1-80DB B8AC6FBE26E1
1BE8D85E-63D1-11E1-80DB B8AC6FBE26E1
1BE8D85F-63D1-11E1-80DB B8AC6FBE26E1
1BE8D860-63D1-11E1-80DB B8AC6FBE26E1
1BE8D861-63D1-11E1-80DB B8AC6FBE26E1
1BE8D862-63D1-11E1-80DB B8AC6FBE26E1
1BE8D863-63D1-11E1-80DB B8AC6FBE26E1
1BE8D864-63D1-11E1-80DB B8AC6FBE26E1
1BE8D865-63D1-11E1-80DB B8AC6FBE26E1
220FB46C-63D1-11E1-80DB B8AC6FBE26E1

Поэтому, если вы надеетесь, что разные компьютеры будут генерировать направляющие, которые "близки" друг к другу, вы будете разочарованы.

Давайте посмотрим на остальные значения.

Семь с половиной байтов из оставшихся 10 байтов являютсяметкой времени; количество интервалов 100 нс с 00:00:00 15 октября 1582 г. Переставляем эти байты меток времени вместе:

Timestamp              Node
=============== ====== ============
1E163D11BE8D85D 1-80DB B8AC6FBE26E1
1E163D11BE8D85E 1-80DB B8AC6FBE26E1
1E163D11BE8D85F 1-80DB B8AC6FBE26E1
1E163D11BE8D860 1-80DB B8AC6FBE26E1
1E163D11BE8D861 1-80DB B8AC6FBE26E1
1E163D11BE8D862 1-80DB B8AC6FBE26E1
1E163D11BE8D863 1-80DB B8AC6FBE26E1
1E163D11BE8D864 1-80DB B8AC6FBE26E1
1E163D11BE8D865 1-80DB B8AC6FBE26E1
1E163D1220FB46C 1-80DB B8AC6FBE26E1

Вы можете увидеть, что guid создан на той же машинеUuidCreateSequentialбудут вместе, так как они хронологические.


1Вы видитеномер версии, в данном случае означающий uuid,основанный на времени. Есть 5 определенных версий:

  • 1: версия на основе времени (UuidCreateSequential)
  • 2: версия DCE Security со встроенными идентификаторами POSIX
  • 3: основанная на имени версия, которая использует хеширование MD5
  • 4: случайно или псевдослучайно сгенерированная версия (UuidCreate)
  • 5: основанная на имени версия, которая использует хэширование SHA-1

Предоставление:

Timestamp       Version      Node
=============== ======= ==== ============
1E163D11BE8D85D 1       80DB B8AC6FBE26E1
1E163D11BE8D85E 1       80DB B8AC6FBE26E1
1E163D11BE8D85F 1       80DB B8AC6FBE26E1
1E163D11BE8D860 1       80DB B8AC6FBE26E1
1E163D11BE8D861 1       80DB B8AC6FBE26E1
1E163D11BE8D862 1       80DB B8AC6FBE26E1
1E163D11BE8D863 1       80DB B8AC6FBE26E1
1E163D11BE8D864 1       80DB B8AC6FBE26E1
1E163D11BE8D865 1       80DB B8AC6FBE26E1
1E163D1220FB46C 1       80DB B8AC6FBE26E1

Последнееслово содержит две вещи.

Младшие 12 бит - это машинно-определяемый номер тактовой последовательности:

Timestamp       Version   Clock Sequence   Node
=============== ======= = ================ ============
1E163D11BE8D85D 1       8 0DB              B8AC6FBE26E1
1E163D11BE8D85E 1       8 0DB              B8AC6FBE26E1
1E163D11BE8D85F 1       8 0DB              B8AC6FBE26E1
1E163D11BE8D860 1       8 0DB              B8AC6FBE26E1
1E163D11BE8D861 1       8 0DB              B8AC6FBE26E1
1E163D11BE8D862 1       8 0DB              B8AC6FBE26E1
1E163D11BE8D863 1       8 0DB              B8AC6FBE26E1
1E163D11BE8D864 1       8 0DB              B8AC6FBE26E1
1E163D11BE8D865 1       8 0DB              B8AC6FBE26E1
1E163D1220FB46C 1       8 0DB              B8AC6FBE26E1

Это постоянное значение для всей машины увеличивается, если:

  • вы переключили сетевые карты
  • вы сгенерировали UUID менее 100 нс из последнего (и метка времени столкнулась бы)

Итак, опять же, любой гид созданUuidCreateSequentialбудет (в идеале) иметь один и тот жепорядковый номер часов, что делает их "рядом" друг с другом.

Последние 2 бита называются Variant и всегда устанавливаются в двоичный 10:

Timestamp       Version Variant Clock Sequence   Node
=============== ======= ======= ================ ============
1E163D11BE8D85D 1       8       0DB              B8AC6FBE26E1
1E163D11BE8D85E 1       8       0DB              B8AC6FBE26E1
1E163D11BE8D85F 1       8       0DB              B8AC6FBE26E1
1E163D11BE8D860 1       8       0DB              B8AC6FBE26E1
1E163D11BE8D861 1       8       0DB              B8AC6FBE26E1
1E163D11BE8D862 1       8       0DB              B8AC6FBE26E1
1E163D11BE8D863 1       8       0DB              B8AC6FBE26E1
1E163D11BE8D864 1       8       0DB              B8AC6FBE26E1
1E163D11BE8D865 1       8       0DB              B8AC6FBE26E1
1E163D1220FB46C 1       8       0DB              B8AC6FBE26E1

Так что у вас есть это. Последовательные руководства являются последовательными; и если вы создадите их на одной машине, они будут "рядом" друг с другом в базе данных.


Но вы хотите знать, что на самом деле происходит с двумя последовательными UUID, созданными на разных компьютерах.

Используя наши новые знания о руководствах версии 1, давайте создадим два руководства для одной и той же временной метки с разных машин, например:

{1BE8D85D-63D1-11E1-80DB-B8AC6FBE26E1}
{1BE8D85D-63D1-11E1-80DB-123456789ABC}

Сначала давайте вставим связку guid с последовательными временными метками. Сначала создайте временную таблицу для хранения наших guid'ов и кластеризируйте по guid:

--DROP table #uuidOrderingTest
CREATE TABLE #uuidOrderingTest
( 
    uuid uniqueidentifier not null
)

CREATE clustered index IX_uuidorderingTest_uuid ON #uuidOrderingTest 
( 
   uuid
)

Теперь вставьте данные:

INSERT INTO #uuidOrderingTest (uuid) VALUES ('{1BE8D866-63D1-11E1-80DB-B8AC6FBE26E1}')
INSERT INTO #uuidOrderingTest (uuid) VALUES ('{1BE8D862-63D1-11E1-80DB-B8AC6FBE26E1}')
INSERT INTO #uuidOrderingTest (uuid) VALUES ('{1BE8D861-63D1-11E1-80DB-B8AC6FBE26E1}')
INSERT INTO #uuidOrderingTest (uuid) VALUES ('{1BE8D85E-63D1-11E1-80DB-B8AC6FBE26E1}')
INSERT INTO #uuidOrderingTest (uuid) VALUES ('{1BE8D864-63D1-11E1-80DB-B8AC6FBE26E1}')
INSERT INTO #uuidOrderingTest (uuid) VALUES ('{1BE8D863-63D1-11E1-80DB-B8AC6FBE26E1}')
INSERT INTO #uuidOrderingTest (uuid) VALUES ('{1BE8D85F-63D1-11E1-80DB-B8AC6FBE26E1}')
INSERT INTO #uuidOrderingTest (uuid) VALUES ('{1BE8D85D-63D1-11E1-80DB-B8AC6FBE26E1}')
INSERT INTO #uuidOrderingTest (uuid) VALUES ('{1BE8D865-63D1-11E1-80DB-B8AC6FBE26E1}')
INSERT INTO #uuidOrderingTest (uuid) VALUES ('{1BE8D860-63D1-11E1-80DB-B8AC6FBE26E1}')

Примечание. Я вставляю их в случайном порядке, чтобы проиллюстрировать, что SQL Server будет кластеризовать их.

Верните строки назад и посмотрите, в каком порядке они расположены: в последовательности (отметка времени):

SELECT * FROM #uuidOrderingTest

uuid
------------------------------------
1BE8D85D-63D1-11E1-80DB-B8AC6FBE26E1
1BE8D85E-63D1-11E1-80DB-B8AC6FBE26E1
1BE8D85F-63D1-11E1-80DB-B8AC6FBE26E1
1BE8D860-63D1-11E1-80DB-B8AC6FBE26E1
1BE8D861-63D1-11E1-80DB-B8AC6FBE26E1
1BE8D862-63D1-11E1-80DB-B8AC6FBE26E1
1BE8D863-63D1-11E1-80DB-B8AC6FBE26E1
1BE8D864-63D1-11E1-80DB-B8AC6FBE26E1
1BE8D865-63D1-11E1-80DB-B8AC6FBE26E1
1BE8D866-63D1-11E1-80DB-B8AC6FBE26E1

Теперь давайте вставим guid с:

  • те же временные метки
  • но другой узел (т.е. MAC-адрес):

Вставьте новые направляющие с "другого" компьютера:

INSERT INTO #uuidOrderingTest (uuid) VALUES ('{1BE8D866-63D1-11E1-80DB-123456789ABC}')
INSERT INTO #uuidOrderingTest (uuid) VALUES ('{1BE8D862-63D1-11E1-80DB-123456789ABC}')
INSERT INTO #uuidOrderingTest (uuid) VALUES ('{1BE8D861-63D1-11E1-80DB-123456789ABC}')
INSERT INTO #uuidOrderingTest (uuid) VALUES ('{1BE8D85E-63D1-11E1-80DB-123456789ABC}')
INSERT INTO #uuidOrderingTest (uuid) VALUES ('{1BE8D864-63D1-11E1-80DB-123456789ABC}')
INSERT INTO #uuidOrderingTest (uuid) VALUES ('{1BE8D863-63D1-11E1-80DB-123456789ABC}')
INSERT INTO #uuidOrderingTest (uuid) VALUES ('{1BE8D85F-63D1-11E1-80DB-123456789ABC}')
INSERT INTO #uuidOrderingTest (uuid) VALUES ('{1BE8D85D-63D1-11E1-80DB-123456789ABC}')
INSERT INTO #uuidOrderingTest (uuid) VALUES ('{1BE8D865-63D1-11E1-80DB-123456789ABC}')
INSERT INTO #uuidOrderingTest (uuid) VALUES ('{1BE8D860-63D1-11E1-80DB-123456789ABC}')

И получите результаты:

uuid
------------------------------------
1BE8D85D-63D1-11E1-80DB-123456789ABC
1BE8D85E-63D1-11E1-80DB-123456789ABC
1BE8D85F-63D1-11E1-80DB-123456789ABC
1BE8D860-63D1-11E1-80DB-123456789ABC
1BE8D861-63D1-11E1-80DB-123456789ABC
1BE8D862-63D1-11E1-80DB-123456789ABC
1BE8D863-63D1-11E1-80DB-123456789ABC
1BE8D864-63D1-11E1-80DB-123456789ABC
1BE8D865-63D1-11E1-80DB-123456789ABC
1BE8D866-63D1-11E1-80DB-123456789ABC
1BE8D85D-63D1-11E1-80DB-B8AC6FBE26E1
1BE8D85E-63D1-11E1-80DB-B8AC6FBE26E1
1BE8D85F-63D1-11E1-80DB-B8AC6FBE26E1
1BE8D860-63D1-11E1-80DB-B8AC6FBE26E1
1BE8D861-63D1-11E1-80DB-B8AC6FBE26E1
1BE8D862-63D1-11E1-80DB-B8AC6FBE26E1
1BE8D863-63D1-11E1-80DB-B8AC6FBE26E1
1BE8D864-63D1-11E1-80DB-B8AC6FBE26E1
1BE8D865-63D1-11E1-80DB-B8AC6FBE26E1
1BE8D866-63D1-11E1-80DB-B8AC6FBE26E1

Так что у вас есть это. Узел заказа SQL Server перед отметкой времени. Uuid, созданный на разных машинах, не будет сгруппирован вместе. Было бы лучше, если бы это не было сделано, но что ты собираешься делать?

Вместо того чтобы полагаться на Win32 API, я обычно использую свой собственный вариант последовательного guid, который заменяет восемь байтов стандартного guid тиками из datetime.

var guidBinary = new byte[16];
Array.Copy( Guid.NewGuid().ToByteArray(), 0, guidBinary, 0, 8 );
Array.Copy( BitConverter.GetBytes( DateTime.Now.Ticks ), 0, guidBinary, 8, 8 );
return new Guid( guidBinary );

Я изменил Томаса, ответьте, чтобы первые 8 байтов были добавлены

 var guidBinary = new byte[16];
 Array.Copy(BitConverter.GetBytes(DateTime.Now.Ticks), 0, guidBinary, 0, 8);
 Array.Copy(Guid.NewGuid().ToByteArray(), 8, guidBinary, 8, 8);
 return new Guid(guidBinary);

и результат будет что-то вроде

b0c99468-714a-08d4-88bd-39e0b53455fb
b122b4b8-714a-08d4-9b12-924e850ad2fe
b1254cf0-714a-08d4-b7c9-954d36290ce5
b12573ff-714a-08d4-b000-632c3a58874d

Вы можете иметь центральную таблицу, в которой есть последний известный начальный UID для диапазона, и увеличивать его.

Например: DB1 создает GUID {AA333F14-FCCD-4bee-9F8F-9D9BDF1B8766} и записывает это в таблицу. DB2 подключается к сети и видит {AA333F14-FCCD-4bee-9F8F-9D9BDF1B8766} и увеличивает его на некоторое установленное число, например, 1 000 000 000 000 000 или что-то действительно высокое, чтобы у вас не было перекрывающихся значений.

Но на самом деле GUID практически бесполезны, когда используются постепенно.

Я думаю, на самом деле вопрос в том, для чего вы используете GUID? Если вы хотите увеличить число, просто используйте 64-битное int(иначе, bigint)

Не уверен насчет способа Win32, но вы можете использовать "недокументированный" newSequentialID() в MSSQL, если у вас есть подключение к базе данных MSSQL.

Я говорю "недокументированный", потому что он считается неверным при попытке сохранить его в качестве значения по умолчанию для столбца MSSQL Identity, и вы должны переопределить его и сказать, что хотите его использовать.

Другие вопросы по тегам