Последовательный GUID в Linq-to-Sql? - PullRequest
20 голосов
/ 20 марта 2009

Я только что прочитал сообщение в блоге о способности NHibernate создавать GUID из системного времени (Guid.Comb), что позволяет избежать значительной фрагментации базы данных. Вы могли бы назвать это на стороне клиента эквивалентом последовательного идентификатора SQL Server.

Есть ли способ использовать подобную стратегию в моем проекте Linq-to-Sql (сгенерировав Guid в коде)?

Ответы [ 6 ]

50 голосов
/ 03 февраля 2010

C # (безопасный) код (Комплименты от NHibernate Guid Comb Generator)

Guid GenerateComb()
{
    byte[] destinationArray = Guid.NewGuid().ToByteArray();
    DateTime time = new DateTime(0x76c, 1, 1);
    DateTime now = DateTime.Now;
    TimeSpan span = new TimeSpan(now.Ticks - time.Ticks);
    TimeSpan timeOfDay = now.TimeOfDay;
    byte[] bytes = BitConverter.GetBytes(span.Days);
    byte[] array = BitConverter.GetBytes((long) (timeOfDay.TotalMilliseconds / 3.333333));
    Array.Reverse(bytes);
    Array.Reverse(array);
    Array.Copy(bytes, bytes.Length - 2, destinationArray, destinationArray.Length - 6, 2);
    Array.Copy(array, array.Length - 4, destinationArray, destinationArray.Length - 4, 4);
    return new Guid(destinationArray);
}

Ссылка на источник на github: https://github.com/nhibernate/nhibernate-core/blob/master/src/NHibernate/Id/GuidCombGenerator.cs

9 голосов
/ 20 марта 2009

COMB генерируются следующим образом:

DECLARE @aGuid UNIQUEIDENTIFIER

SET @aGuid = CAST(CAST(NEWID() AS BINARY(10)) + CAST(GETDATE() AS BINARY(6)) AS UNIQUEIDENTIFIER)

То, что транскрибируется в C #, будет выглядеть так:

    public static unsafe Guid CombGuid()
    {
        Guid guid = Guid.NewGuid();
        byte[] bytes = guid.ToByteArray();
        long ticks = DateTime.Now.Ticks;
        fixed( byte* pByte = bytes )
        {
            int*    pFirst  = (int *)(pByte + 10);
            short* pNext    = (short*)(pByte + 14);
            *pFirst = (int)(ticks & 0xFFFFFF00);
            *pNext  = (short)ticks;
        }

        return new Guid( bytes );
    }
3 голосов
/ 03 февраля 2010

Вы всегда можете вызвать UuidCreateSequential; это «старый» генератор guid (до 2000 года, когда MSFT изменил его на более случайные руководства по стилю, к которым мы привыкли сегодня). Они переименовали старый UuidCreate в UuidCreateSequential и поместили свой новый генератор guid в новую реализацию UuidCreate. UuidCreateSequential - это также то, что SQL Server использует в NewSequentialID (), и он также уникален, как и обычные направляющие, но с преимуществом, что они последовательны, если вы создаете их кучу в строке в одном процессе.

using System;
using System.Runtime.InteropServices;

namespace System
{
    public static class GuidEx
    {
        [DllImport("rpcrt4.dll", SetLastError = true)]
        private static extern int UuidCreateSequential(out Guid guid);
        private const int RPC_S_OK = 0;

        /// <summary>
        /// Generate a new sequential GUID. If UuidCreateSequential fails, it will fall back on standard random guids.
        /// </summary>
        /// <returns>A GUID</returns>
        public static Guid NewSeqGuid()
        {
            Guid sequentialGuid;
            int hResult = UuidCreateSequential(out sequentialGuid);
            if (hResult == RPC_S_OK)
            {
                return sequentialGuid;
            }
            else
            {
                //couldn't create sequential guid, fall back on random guid
                return Guid.NewGuid();
            }
        }
    }
}
3 голосов
/ 20 марта 2009

Ну, вы можете сгенерировать Guid вручную. Тем не менее, одно из преимуществ Guid заключается в том, что его нельзя догадаться - то есть, учитывая запись 0000-...-0005, обычно нет смысла (от атакующего) проверять запись 0000-....-0004 и т. Д.

Также - повторная фрагментация? Пока у вас есть некластеризованный индекс для этих данных, я не уверен, что это проблема. Обычно вы не помещаете кластеризованный индекс в Guid, поэтому таблица будет кучей (если у вас нет отдельного кластеризованного индекса, такого как IDENTITY int). В этом случае вы будете добавлять в конец и вставлять новый Guid в некластеризованный индекс. Никакой настоящей боли.

(редактировать) Одной из проблем непосредственного использования времени является то, что вы привносите гораздо больший риск столкновений; вам нужно было бы беспокоиться о создании «тесного цикла» Guid (то есть избегать повторения при создании нескольких в последовательности), что означает синхронизацию и т. д. получу дубликаты.

2 голосов
/ 07 июня 2010

@ Arul, @ Doug

Почему вы указали часть времени в конце GUID?

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

Хорошо, я нашел ответ , и этот ответ от Бернарда Кирхера и сайта Сравнение значений GUID и уникального идентификатора (ADO.NET) , на которые он ссылается .

Таким образом, GUID, сгенерированные таким образом, не будут работать так же, как в других базах данных, кроме MS SQL-Server, но это не связано с LINQ-to-SQL.

Извините за деформированные URL, но у меня недостаточно репутации, чтобы публиковать дополнительные ссылки.

0 голосов
/ 20 сентября 2012

Мы использовали метод, аналогичный тому, который Дуг опубликовал выше в модели Entity Framework, поэтому вы должны быть в состоянии сделать это, используя Linq to SQL.

Для этого нам понадобился генератор направляющих гребня для тестирования, и в итоге мы создали этот небольшой инструмент для генерации направляющих гребня онлайн

http://www.webdesigncompany.co.uk/comb-guid/

Надеюсь, это вам тоже поможет.

...