SQL 服务器 CLR 集成未按预期调用系统时间
SQL Server CLR Integration not calling system time as expected
先了解一下背景,然后才是具体问题。
我正在尝试生成顺序 GUID 以用作 SQL Server 2008 R2 table 中的主要 ID。以前,这些是由用户定义的函数生成的,该函数执行过多的字符串操作以生成顺序 GUID,这是一个性能瓶颈。
为了尝试减少此函数的时间,我尝试使用 CLR 集成并在 C# class 中执行操作。 GUID 的序列化是通过将 GUID 的最后 12 bytes 更新为从系统日期时间生成的值来完成的。
在初始版本中,GetUTCDate() SQL 服务器系统函数用于获取日期时间并将其转换为刻度,然后用于创建 guid 的连续 12 字节.我的方法是使用 .NET Framework DateTime.UtcNow.Ticks
属性 转换为最后 12 个字节,但我注意到 Ticks
属性 没有按预期更新并且这表明系统时间功能没有像我预期的那样经常被调用。
为了对此进行测试,我创建了一个简单的方法,其中 return 刻度线:
[SqlFunction()]
public static long GetTicks()
{
return DateTime.UtcNow.Ticks;
}
此方法的程序集随后创建为 ASSEMBLY object in SQL Server; note that CLR integration was enabled and the assembly was created in UNSAFE
mode thus allowing access to OS APIs。然后,我在 SQL 服务器中创建了一个函数,并为此 CLR 函数创建了一个函数:
CREATE FUNCTION GetTicks() RETURNS BIGINT
AS EXTERNAL NAME Sequential.Generator.GetTicks;
GO
然后我执行了以下两个语句,结果如下所示
SELECT [dbo].[GetTicks]();
SELECT [dbo].[GetTicks]();
结果是:
这表明对 CLR 函数的顺序调用 returned 了相同的 TICKS 值。这意味着对于 GetTicks()
函数的每次调用,未对 return 报价执行基础 OS 调用。然后我通过创建一个简单的 table 进行了第二次测试,如下所示:
CREATE TABLE #tmp1(
[NUM] [INT] NULL,
[TICKS] BIGINT
);
我用 300 行填充了 table,然后执行了以下语句:
UPDATE #tmp1
SET [ticks] = [Play].[dbo].[GetTicks]();
然后我测量了连续行的 [ticks] 值之间的差异,并且在所有 300 行中只有一个值发生变化(在 Excel 中完成):
同样,这表明框架 DateTime.UtcNow.Ticks
属性 未在每次调用 CLR 集成函数时被调用 GetTicks()
。
有没有一种方法可以启用 CLR 集成,以便每次从 SQL 服务器调用函数时都可以执行对底层 OS 的调用?
SQL CLR 集成是否是一种不明智的方法 SQL 服务器从 OS 检索系统时间的频率比 [=22] =] T-SQL函数?
谢谢!
(*) 请注意,顺序 GUID 是必需的,我只是想最大限度地提高它们生成方式的性能。此外,由于隐私问题,NEWSEQUENTIALID() 函数不被接受table。
Again, this indicates the Framework DateTime.UtcNow.Ticks property was not being called for each invocation of the CLR Integrated function GetTicks().
不,这并不表示 UtcNow
属性 没有被调用。问题是 .NET 中 System.DateTime
的解析。无论该值有多精确,该值仅每 10 - 16 毫秒递增一次(类似于 T-SQL 中的 GETDATE()
仅每 3 毫秒更新一次)。这就是为什么该值对于一定数量的行保持不变,但不像 T-SQL 日期时间函数那样跨所有行(例如 GETDATE()
等)。
Is there a way to enable CLR Integration such that calls to the underlying OS can performed with each invocation of a function from SQL Server?
"CLR Integration" 特征是 "enabled" 或 "disabled"。除此之外没有它的配置。正如上面直接提到的,这是 DateTime.UtcNow.Ticks
的值更新频率的问题。
Is SQL CLR Integration an unwise approach for SQL Server to retrieve the system time from the OS on a basis more frequent than the GetUTCDATE() T-SQL function?
不像使用 GUID / UNIQUEIDENTIFIER
作为 PK 那样不明智(我假设它是聚簇索引),这本身并不像试图减轻由此导致的性能问题那样不明智然后通过引入非内置函数来决定。
当前UDF是如何调用生成值的? INSERT
触发器?
如果性能是个问题,那么将 PK 设置为 INT
或 BIGINT
并使用 IDENTITY
。保留 GUID 字段,但通过向其添加 NonClustered 索引使其成为 "alternate key"。应用程序代码可以使用 GUID 值,但 JOIN 等应使用 INT
/ BIGINT
PK。
但是,就 DateTime.UtcNow.Ticks
的更频繁变化的值而言,从 Windows 8 / Windows Server 2012 开始,有一个新的 OS功能,GetSystemTimePreciseAsFileTime,这将适用于此。通常我会添加 "unfortunately it requires that the Assembly be marked as UNSAFE",因为除非绝对必要,否则应该避免这种情况,但您已经提到将您的程序集标记为 UNSAFE
,因此这没有改变。
要使用这个新功能,只需从这里获取代码:
然后替换:
DateTime.UtcNow.Ticks
与:
HighResolutionDateTime.UtcNow.Ticks
!! 请注意: SQL 服务器不保证 调用函数的次数。这意味着,可能,即使极不可能,多次调用此函数(或任何函数,包括 NEWID()
)将 return 相同的值,即使如果明确声明为 IsDeterministic = false
。然而,这也适用于 T-SQL UDF,因此如果进程当前正在使用 T-SQL UDF,那么一旦切换为 SQLCLR 标量函数。
先了解一下背景,然后才是具体问题。
我正在尝试生成顺序 GUID 以用作 SQL Server 2008 R2 table 中的主要 ID。以前,这些是由用户定义的函数生成的,该函数执行过多的字符串操作以生成顺序 GUID,这是一个性能瓶颈。
为了尝试减少此函数的时间,我尝试使用 CLR 集成并在 C# class 中执行操作。 GUID 的序列化是通过将 GUID 的最后 12 bytes 更新为从系统日期时间生成的值来完成的。
在初始版本中,GetUTCDate() SQL 服务器系统函数用于获取日期时间并将其转换为刻度,然后用于创建 guid 的连续 12 字节.我的方法是使用 .NET Framework DateTime.UtcNow.Ticks
属性 转换为最后 12 个字节,但我注意到 Ticks
属性 没有按预期更新并且这表明系统时间功能没有像我预期的那样经常被调用。
为了对此进行测试,我创建了一个简单的方法,其中 return 刻度线:
[SqlFunction()]
public static long GetTicks()
{
return DateTime.UtcNow.Ticks;
}
此方法的程序集随后创建为 ASSEMBLY object in SQL Server; note that CLR integration was enabled and the assembly was created in UNSAFE
mode thus allowing access to OS APIs。然后,我在 SQL 服务器中创建了一个函数,并为此 CLR 函数创建了一个函数:
CREATE FUNCTION GetTicks() RETURNS BIGINT
AS EXTERNAL NAME Sequential.Generator.GetTicks;
GO
然后我执行了以下两个语句,结果如下所示
SELECT [dbo].[GetTicks]();
SELECT [dbo].[GetTicks]();
结果是:
这表明对 CLR 函数的顺序调用 returned 了相同的 TICKS 值。这意味着对于 GetTicks()
函数的每次调用,未对 return 报价执行基础 OS 调用。然后我通过创建一个简单的 table 进行了第二次测试,如下所示:
CREATE TABLE #tmp1(
[NUM] [INT] NULL,
[TICKS] BIGINT
);
我用 300 行填充了 table,然后执行了以下语句:
UPDATE #tmp1
SET [ticks] = [Play].[dbo].[GetTicks]();
然后我测量了连续行的 [ticks] 值之间的差异,并且在所有 300 行中只有一个值发生变化(在 Excel 中完成):
同样,这表明框架 DateTime.UtcNow.Ticks
属性 未在每次调用 CLR 集成函数时被调用 GetTicks()
。
有没有一种方法可以启用 CLR 集成,以便每次从 SQL 服务器调用函数时都可以执行对底层 OS 的调用?
SQL CLR 集成是否是一种不明智的方法 SQL 服务器从 OS 检索系统时间的频率比 [=22] =] T-SQL函数?
谢谢!
(*) 请注意,顺序 GUID 是必需的,我只是想最大限度地提高它们生成方式的性能。此外,由于隐私问题,NEWSEQUENTIALID() 函数不被接受table。
Again, this indicates the Framework DateTime.UtcNow.Ticks property was not being called for each invocation of the CLR Integrated function GetTicks().
不,这并不表示 UtcNow
属性 没有被调用。问题是 .NET 中 System.DateTime
的解析。无论该值有多精确,该值仅每 10 - 16 毫秒递增一次(类似于 T-SQL 中的 GETDATE()
仅每 3 毫秒更新一次)。这就是为什么该值对于一定数量的行保持不变,但不像 T-SQL 日期时间函数那样跨所有行(例如 GETDATE()
等)。
Is there a way to enable CLR Integration such that calls to the underlying OS can performed with each invocation of a function from SQL Server?
"CLR Integration" 特征是 "enabled" 或 "disabled"。除此之外没有它的配置。正如上面直接提到的,这是 DateTime.UtcNow.Ticks
的值更新频率的问题。
Is SQL CLR Integration an unwise approach for SQL Server to retrieve the system time from the OS on a basis more frequent than the GetUTCDATE() T-SQL function?
不像使用 GUID / UNIQUEIDENTIFIER
作为 PK 那样不明智(我假设它是聚簇索引),这本身并不像试图减轻由此导致的性能问题那样不明智然后通过引入非内置函数来决定。
当前UDF是如何调用生成值的? INSERT
触发器?
如果性能是个问题,那么将 PK 设置为 INT
或 BIGINT
并使用 IDENTITY
。保留 GUID 字段,但通过向其添加 NonClustered 索引使其成为 "alternate key"。应用程序代码可以使用 GUID 值,但 JOIN 等应使用 INT
/ BIGINT
PK。
但是,就 DateTime.UtcNow.Ticks
的更频繁变化的值而言,从 Windows 8 / Windows Server 2012 开始,有一个新的 OS功能,GetSystemTimePreciseAsFileTime,这将适用于此。通常我会添加 "unfortunately it requires that the Assembly be marked as UNSAFE",因为除非绝对必要,否则应该避免这种情况,但您已经提到将您的程序集标记为 UNSAFE
,因此这没有改变。
要使用这个新功能,只需从这里获取代码:
然后替换:
DateTime.UtcNow.Ticks
与:
HighResolutionDateTime.UtcNow.Ticks
!! 请注意: SQL 服务器不保证 调用函数的次数。这意味着,可能,即使极不可能,多次调用此函数(或任何函数,包括 NEWID()
)将 return 相同的值,即使如果明确声明为 IsDeterministic = false
。然而,这也适用于 T-SQL UDF,因此如果进程当前正在使用 T-SQL UDF,那么一旦切换为 SQLCLR 标量函数。