Кластерный столбец GUID и newsequentialid на разных серверах
Хорошо известно, что использование случайных значений в столбце с кластеризованным индексом не является хорошей идеей, поэтому использование GUID для первичного ключа с кластеризованным индексом обычно не рекомендуется. Используя функцию newsequentialid(), мы можем преодолеть большинство из этих трудностей.
Однако что произойдет, если вы генерируете GUID на ферме веб-серверов, все из которых попадают в одну базу данных? Я создаю последовательные идентификаторы в коде.NET, используя UuidCreateSequential, как описано в этой статье: http://blogs.msdn.com/b/dbrowne/archive/2012/07/03/how-to-generate-sequential-guids-for-sql-server-in-net.aspx
Проблема заключается в том, что, хотя полученные идентификаторы GUID являются последовательными с одного компьютера, это не относится к нескольким компьютерам. Поскольку наиболее значимые 11 байтов (в соответствии с SQL Server), по-видимому, остаются практически одинаковыми для одной и той же машины, он эффективно сортирует по машине, а затем по времени, а не по желаемой противоположности.
Было бы целесообразным и выполнимым изменить порядок байтов в GUID для получения почти последовательных идентификаторов GUID между компьютерами, или я должен отказаться и сделать индексы некластерными?
Спасибо!
2 ответа
После этого я собираюсь ответить на свой вопрос и сказать, что генерация последовательных идентификаторов GUID (COMB GUID) из нескольких машин, как описано в вопросе, не является проблемой. По сути, у вас будет одна отдельная последовательность идентификаторов для каждого компьютера, что не приведет к разбиению страниц, поскольку они будут добавляться в конец разных страниц, а не в середину страницы (поскольку новый идентификатор всегда будет самым большим в своей последовательности).
Хотя GUID может быть не таким эффективным, как int, у меня не было проблем с использованием этого подхода с миллионами строк в таблице.
Вы также можете сгенерировать свои идентификаторы на C#, взгляните на это сообщение о проекте кода. prb в том, что код, сгенерированный этой реализацией, не совпадает с тем, что генерирует NEWSEQUENTIALID, так как моя цель состояла в том, чтобы код C# генерировал последние 6 байтов Guid как функции NewSequentialID сервера Sql, я получаю следующий код.
public static Guid ToSequentialAtEnd(this Guid guid)
{
byte[] guidArray = guid.ToByteArray();
DateTime now = DateTime.UtcNow;
var baseDate = new DateTime(1900, 1, 1);
// Get the days and milliseconds which will be used to build the byte string
var 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.33333333
byte[] daysArray = BitConverter.GetBytes(days.Days);
byte[] msecsArray = BitConverter.GetBytes((long)(msecs.TotalMilliseconds / 3.33333333));
// 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);
}