Есть ли .NET, эквивалентный SQL Server newsequentialid () - PullRequest
31 голосов
/ 17 октября 2008

Мы используем Guid для первичного ключа, который, как вы знаете, кластеризован по умолчанию.

При вставке новой строки в таблицу она вставляется на случайную страницу в таблице (потому что Guid-ы являются случайными). Это оказывает ощутимое влияние на производительность, поскольку БД постоянно разделяет страницы данных (фрагментация). Но главная причина, по которой я использую последовательный Guid, заключается в том, что я хочу, чтобы новые строки вставлялись как последняя строка в таблицу ... что поможет при отладке.

Я могу создать кластеризованный индекс для CreateDate, но наша БД генерируется автоматически, и в процессе разработки нам нужно сделать что-то дополнительное, чтобы облегчить это. Также CreateDate не является хорошим кандидатом для кластерного индекса.

Раньше я использовал Jimmy Nielsons COMB's , но мне было интересно, есть ли что-то в .NET Framework для этого. В SQL 2005 Microsoft представила newsequentialid () в качестве альтернативы newid (), поэтому я надеялся, что они сделали эквивалент .NET, потому что мы генерируем идентификатор в коде.

PS: Пожалуйста, не начинайте обсуждать, правильно это или нет, потому что GUID должен быть уникальным и т. Д.

Ответы [ 11 ]

24 голосов
/ 17 октября 2008

Должна быть возможность создания последовательного GUID в c # или vb.net с помощью вызова API UuidCreateSequential. Объявление API (C #) ниже взято из Pinvoke.net , где вы также можете найти полный пример того, как вызывать функцию.

[DllImport("rpcrt4.dll", SetLastError=true)]
static extern int UuidCreateSequential(out Guid guid);

Статья MSDN, относящаяся к функции UuidCreateSequential, может быть найдена здесь , которая включает необходимые условия для использования.

19 голосов
/ 25 сентября 2012

Обновление 2018: Также проверьте мой другой ответ

Вот как NHibernate генерирует последовательные идентификаторы:

NHibernate.Id.GuidCombGenerator

/// <summary>
/// Generate a new <see cref="Guid"/> using the comb algorithm.
/// </summary>
private Guid GenerateComb()
{
    byte[] guidArray = Guid.NewGuid().ToByteArray();

    DateTime baseDate = new DateTime(1900, 1, 1);
    DateTime now = DateTime.Now;

    // Get the days and milliseconds which will be used to build the byte string 
    TimeSpan days = new TimeSpan(now.Ticks - baseDate.Ticks);
    TimeSpan msecs = now.TimeOfDay;

    // Convert to a byte array 
    // Note that SQL Server is accurate to 1/300th of a millisecond so we divide by 3.333333 
    byte[] daysArray = BitConverter.GetBytes(days.Days);
    byte[] msecsArray = BitConverter.GetBytes((long) (msecs.TotalMilliseconds / 3.333333));

    // Reverse the bytes to match SQL Servers ordering 
    Array.Reverse(daysArray);
    Array.Reverse(msecsArray);

    // Copy the bytes into the guid 
    Array.Copy(daysArray, daysArray.Length - 2, guidArray, guidArray.Length - 6, 2);
    Array.Copy(msecsArray, msecsArray.Length - 4, guidArray, guidArray.Length - 4, 4);

    return new Guid(guidArray);
}
9 голосов
/ 17 октября 2008

Возможно, простым способом определения порядка, в котором были добавлены строки, было бы добавить столбец IDENTITY в таблицу, избегая необходимости поддерживать порядок в GUIDE и, следовательно, избегая снижения производительности при поддержании кластеризованного индекса в Столбец GUID.

Я не могу не задаться вопросом, как поддержание порядка в этих строках помогает вам при отладке. Не могли бы вы немного это расширить?

5 голосов
/ 06 декабря 2017

Важно отметить, что идентификаторы UUID, сгенерированные UuidCreateSequential , не будут последовательными при заказе SQL Server.

  • SQL Server следует RFC , когда дело доходит до сортировки UUID
  • RFC понял неправильно
  • UuidCreateSequential все сделал правильно
  • но UuidCreateSequential создает нечто отличное от того, что ожидает SQL Server

Фон * * тысяча двадцать один UUID типа 1, созданные UuidCreateSequential, не сортируются в SQL Server. SQL Server NewSequentialID использует UuidCreateSequential с применением некоторой перестановки байтов. Из книг онлайн: NEWSEQUENTIALID (Transact-SQL) NEWSEQUENTIALID - это оболочка для функции Windows UuidCreateSequential, с применено некоторое перемешивание байтов , который затем ссылается на сообщение в блоге MSDN: Как создать последовательные идентификаторы GUID для SQL Server в .NET ( архив ) public static Guid NewSequentialId() { Guid guid; UuidCreateSequential(out guid); var s = guid.ToByteArray(); var t = new byte[16]; t[3] = s[0]; t[2] = s[1]; t[1] = s[2]; t[0] = s[3]; t[5] = s[4]; t[4] = s[5]; t[7] = s[6]; t[6] = s[7]; t[8] = s[8]; t[9] = s[9]; t[10] = s[10]; t[11] = s[11]; t[12] = s[12]; t[13] = s[13]; t[14] = s[14]; t[15] = s[15]; return new Guid(t); } Все начинается с количества тиков, начиная с 1582-10-15 00:00:00 (15 октября 1592 г., дата Григорианская реформа по христианскому календарю). Тики - это число интервалов в 100 нс. Например: 12/6 / 2017 4:09:39 UTC = 137 318 693 794 503 714 тиков = 0x01E7DA9FDCA45C22 галочки RFC говорит, что мы должны разделить это значение на три части: UInt32, низкий (4 байта) Uint16 mid (2 байта) UInt32 hi (2 байта) Итак, мы разделили это: 0x01E7DA9FDCA45C22 | Hi | Mid | Low | |--------|--------|------------| | 0x01E7 | 0xDA9F | 0xDCA45C22 | И тогда RFC говорит, что эти три целых числа должны быть записаны в следующем порядке: Низкий: 0xDCA45C22 Середина: 0xDA9F Высокий: 0x01E7 Если вы следуете RFC, эти значения должны быть записаны в big-endian (он же "сетевой порядок байтов" ): DC A4 5C 22 DA 9F x1 E7 xx xx xx xx xx xx xx xx Это был плохой дизайн, потому что вы не можете взять первые 8 байтов UUID и рассматривать их как UInt64 с прямым порядком байтов или как UInt64 с прямым порядком байтов. Это совершенно тупое кодирование. UuidCreateSequential делает все правильно Microsoft до сих пор придерживалась тех же правил: Низкий: 0xDCA45C22 Середина: 0xDA9F Высокий: 0x1E7 Но они пишут это в Intel little-endian заказ: 22 5C A4 DC 9F DA E7 x1 xx xx xx xx xx xx xx xx Если вы посмотрите на это, вы только что написали порядковый номер Int64: 225CA4DC9FDAE701 Значение: если вы хотите извлечь метку времени или сортировка по отметке времени это тривиально; просто обработайте первые 8 байтов как UInt64. С RFC у вас нет выбора, кроме как выполнять всевозможные операции с битами. Даже на машинах с прямым порядком байтов 64-разрядная временная метка не может рассматриваться как 64-разрядная временная метка. Как поменять его С учетом порядка байтов от UuidCreateSequential: DCA45C22-DA9F-11E7-DDDD-FFFFFFFFFFFF с необработанными байтами: 22 5C A4 DC 9F DA E7 11 DD DD FF FF FF FF FF FF Это расшифровывается в: Low Mid Version High -------- ---- ------- ---- ----------------- DCA45C22-DA9F-1 1E7 -DDDD-FFFFFFFFFFFF Низкий: 0xDCA45C22 Середина: 0xDA9F Высокий: 0x1E7 Версия: 1 (тип 1) Мы можем выписать это обратно в порядке RFC big-endian: DC A4 5C 22 DA 9F 11 E7 DD DD FF FF FF FF FF FF Короткая версия | Swap | Swap | Swap | Copy as-is Start index | 0 1 2 3 | 4 5 | 6 7 | End index | 3 2 1 0 | 5 4 | 7 6 | ---------------|-------------|-------|-------|------------------------ Little-endian: | 22 5C A4 DC | 9F DA | E7 11 | DD DD FF FF FF FF FF FF Big-endian: | DC A4 5C 22 | DA 9F | 11 E7 | DD DD FF FF FF FF FF FF

2 голосов
/ 13 марта 2018

Для этого вы можете использовать крошечную библиотеку NewId.

Установите его через NuGet:

Install-Package NewId

И используйте это так:

Guid myNewSequentialGuid =  NewId.NextGuid();

См. Страница проекта на GitHub

2 голосов
/ 22 августа 2009

Вот код C # для генерации GUID COMB.

byte[] guidArray = System.Guid.NewGuid().ToByteArray();

DateTime baseDate = new DateTime(1900, 1, 1);
DateTime now = DateTime.Now;

// Get the days and milliseconds which will be used to build the byte string 
TimeSpan days = new TimeSpan(now.Ticks - baseDate.Ticks);
TimeSpan msecs = new TimeSpan(now.Ticks - (new DateTime(now.Year, now.Month, now.Day).Ticks));

// Convert to a byte array 
// Note that SQL Server is accurate to 1/300th of a millisecond so we divide by 3.333333 
byte[] daysArray = BitConverter.GetBytes(days.Days);
byte[] msecsArray = BitConverter.GetBytes((long)(msecs.TotalMilliseconds / 3.333333));

// Reverse the bytes to match SQL Servers ordering 
Array.Reverse(daysArray);
Array.Reverse(msecsArray);

// Copy the bytes into the guid 
Array.Copy(daysArray, daysArray.Length - 2, guidArray, guidArray.Length - 6, 2);
Array.Copy(msecsArray, msecsArray.Length - 4, guidArray, guidArray.Length - 4, 4);

return new System.Guid(guidArray);
2 голосов
/ 17 октября 2008

К сожалению, нет .NET не эквивалентен newsequentialid(). Вы могли бы продолжать использовать Гребень. У меня на самом деле где-то есть реализация Comb на C # ... Я посмотрю, смогу ли я ее выкопать.

1 голос
/ 10 августа 2012

Для людей, которые специально используют Entity Framework , Вы можете сохранить хранимую процедуру на сервере , чтобы создать новый последовательный идентификатор и вернуть идентификатор. Затем вы можете использовать этот последовательный идентификатор для заполнения вашей другой таблицы . Я думаю, что это должно работать.

0 голосов
/ 14 октября 2010

О выбранном ответе. В документации сказано ... Сгенерированный Guid не даст вам уникальный идентификатор между компьютерами, если у них нет доступа к Интернету.

Если вы должны знать guid при вставке, не могли бы вы позволить Sql-серверу вернуть блок последовательных направляющих, которые вы назначаете своим данным, прежде чем вставлять их?

declare @ids table(id uniqueidentifier default NEWSEQUENTIALID(), dummy char(1))

declare @c int
set @c = 0;
while (@c < 100)
begin
    insert into @ids (dummy) values ('a');
    set @c += 1;
end

select id from @ids
0 голосов
/ 17 октября 2008

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

Предложения Джона PInvoke, вероятно, наиболее близки к версии SQL, но в документе UUidCreateSequential указывается, что его не следует использовать для идентификации объекта, строго привязанного к машине, генерирующей Guid.

Я бы измерил фактический показатель производительности при использовании реалистичных данных в реалистичных количествах, прежде чем я продолжу исследовать последовательное создание Guid.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...