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

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

Можно ли использовать аналогичную стратегию в моем проекте Linq-to-Sql (путем генерации кода в коде)?

Ответ 1

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

DECLARE @aGuid UNIQUEIDENTIFIER

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

Что транскрибируется на С# будет выглядеть так:

    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 );
    }

Ответ 2

С# (безопасный) код (комплименты генератора гребня NHibernate Guid)

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

Ответ 3

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

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

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

Ответ 4

Вы всегда можете вызвать UuidCreateSequential; это "старый" генератор guid (pre-2000-ish, когда MSFT изменил его на более случайные стили, которые мы используем сегодня). Они переименовали старый UuidCreate в UuidCreateSequential и поместили свой новый генератор ключей в новую реализацию 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();
            }
        }
    }
}

Ответ 5

@arul, @Doug

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

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

Хорошо, я нашел ответ , и этот ответ от Bernhard Kircher и сайт Сравнение значений GUID и uniqueidentifier (ADO.NET), на которые он ссылается.

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

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

Ответ 6

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

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

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

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