跨服务器的集群 GUID 列和 newsequentialid
Clustered GUID column and newsequentialid across servers
众所周知,在具有聚集索引的列中使用随机值不是一个好主意,这就是为什么通常不建议对具有聚集索引的主键使用 GUID。使用newsequentialid()函数,我们可以克服大部分困难。
但是,如果您在一组 Web 服务器上生成 GUID,并且所有服务器访问同一个数据库,会发生什么情况?我正在使用 UuidCreateSequential 在 .NET 代码中创建顺序 ID,如本文所述:
http://blogs.msdn.com/b/dbrowne/archive/2012/07/03/how-to-generate-sequential-guids-for-sql-server-in-net.aspx
问题在于,虽然生成的 GUID 是来自单台机器的顺序,但跨多台机器时情况并非如此。因为最重要的 11 个字节(根据 SQL 服务器)对于同一台机器似乎保持几乎相同,所以它有效地按机器然后时间排序,而不是所需的相反。
重新排序 GUID 中的字节以在机器之间获得接近顺序的 GUID 是否值得并且可行,还是我应该放弃并使索引成为非聚集索引?
谢谢!
在尝试这个之后,我将回答我自己的问题,并说从问题中描述的多台机器生成顺序 GUID (COMB GUID) 不是问题。本质上,每台机器都有一个单独的 ID 序列,这不会导致页面拆分,因为它们将附加到不同页面的末尾,而不是在页面中间(因为新 ID 将始终是最大的按其顺序)。
虽然 GUID 可能不如 int 高效,但我在使用这种方法处理每个 table 数百万行时没有遇到任何问题。
你也可以在 c# 上生成你的 id,看看 this post on code project prb 是这个实现生成的代码与 NEWSEQUENTIALID 生成的代码不匹配,因为我的目标是 c# 代码将生成 Guid 的最后 6 个字节作为 Sql 服务器的 NewSequentialID 函数,我最终得到以下代码。
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);
}
众所周知,在具有聚集索引的列中使用随机值不是一个好主意,这就是为什么通常不建议对具有聚集索引的主键使用 GUID。使用newsequentialid()函数,我们可以克服大部分困难。
但是,如果您在一组 Web 服务器上生成 GUID,并且所有服务器访问同一个数据库,会发生什么情况?我正在使用 UuidCreateSequential 在 .NET 代码中创建顺序 ID,如本文所述: http://blogs.msdn.com/b/dbrowne/archive/2012/07/03/how-to-generate-sequential-guids-for-sql-server-in-net.aspx
问题在于,虽然生成的 GUID 是来自单台机器的顺序,但跨多台机器时情况并非如此。因为最重要的 11 个字节(根据 SQL 服务器)对于同一台机器似乎保持几乎相同,所以它有效地按机器然后时间排序,而不是所需的相反。
重新排序 GUID 中的字节以在机器之间获得接近顺序的 GUID 是否值得并且可行,还是我应该放弃并使索引成为非聚集索引?
谢谢!
在尝试这个之后,我将回答我自己的问题,并说从问题中描述的多台机器生成顺序 GUID (COMB GUID) 不是问题。本质上,每台机器都有一个单独的 ID 序列,这不会导致页面拆分,因为它们将附加到不同页面的末尾,而不是在页面中间(因为新 ID 将始终是最大的按其顺序)。
虽然 GUID 可能不如 int 高效,但我在使用这种方法处理每个 table 数百万行时没有遇到任何问题。
你也可以在 c# 上生成你的 id,看看 this post on code project prb 是这个实现生成的代码与 NEWSEQUENTIALID 生成的代码不匹配,因为我的目标是 c# 代码将生成 Guid 的最后 6 个字节作为 Sql 服务器的 NewSequentialID 函数,我最终得到以下代码。
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);
}