Последовательный GUID в Linq-to-Sql?

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

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


person JacobE    schedule 20.03.2009    source источник
comment
Я пробовал все эти образцы руководств COMB, и все они генерируют высокую фрагментацию с 10К строками. версия arul - фрагментация 98%. ‹br/› NHibernate - фрагментация 53%. ‹br/› rpcrt4.dll - фрагментация 98% ‹br/› bigint - фрагментация 6% ‹br/› Насколько хорошо люди ожидают, что товары COMB будут работать ?   -  person Shaun    schedule 05.12.2011
comment
@Shaun это зависит от того, насколько быстро вы вставляете, если у вас всего несколько вставок в секунду, фрагментации быть не должно.   -  person Peter    schedule 08.07.2014


Ответы (6)


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

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 );
    }
person arul    schedule 20.03.2009
comment
Это было бы очень полезно для меня, но нужно ли мне компилировать всю мою программу с / unsafe или я могу поместить это в свою собственную библиотеку классов и скомпилировать только это с небезопасным флагом? - person Refracted Paladin; 20.04.2009
comment
@Paladin: Вы можете сделать это, не прибегая к небезопасному коду. Взгляните на класс BitConvert - person R. Martinho Fernandes; 03.01.2010
comment
См. Мой ответ ниже, чтобы узнать о безопасной версии создания Guid.Comb. - person Doug; 06.02.2010

Код C # (безопасный) (Комплименты генератора гребешков NHibernate)

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

person Doug    schedule 02.02.2010
comment
Только что добавился в мой проект! - person Chris Marisic; 28.07.2010
comment
Рад слышать это. Я нашел эту маленькую жемчужину в исходнике NHibernate и просто должен был ею поделиться :) - person Doug; 20.11.2010

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

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

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

person Marc Gravell    schedule 20.03.2009
comment
Решение, которое я предложил в качестве правильного ответа, сочетает в себе произвольный Guid с сгенерированной по времени частью, что устраняет риск дублирования. Я не знаю, решает ли он какие-либо проблемы с фрагментацией ... - person JacobE; 20.03.2009

Вы всегда можете вызвать UuidCreateSequential; это «старый» генератор руководств (до 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();
            }
        }
    }
}
person KristoferA    schedule 03.02.2010
comment
К вашему сведению, если вы используете Mono, то rpcrt4.dll не будет существовать, и это не сработает. - person Doug; 12.05.2010
comment
Хорошее замечание, Дуг. Я использую UuidCreateSequential, но забыл об этой оснастке. - person granadaCoder; 23.08.2013
comment
Что делать, если машина перезапустилась ?? тогда вы потеряете последовательный - person Wahid Bitar; 19.09.2014
comment
@WahidBitar для фрагментации, это вряд ли имеет значение ... ... если только вы не перезапускаете очень часто ... - person KristoferA; 19.09.2014

@arul, @Doug

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

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

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

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

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

person user285085    schedule 07.06.2010

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

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

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

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

person leen3o    schedule 20.09.2012