c# - SQL Server CLR 集成未按预期调用系统时间

标签 c# .net sql-server sqlclr

首先介绍一些背景信息,然后才是具体问题。

我正在尝试生成连续的 GUID,以用作 SQL Server 2008 R2 表中的主 ID。以前,这些是由用户定义的函数生成的,该函数执行过多的字符串操作来生成顺序 GUIDS,这是一个性能瓶颈。

为了尝试减少此函数的时间,我尝试使用 CLR 集成并在 C# 类中执行操作。 GUID 的顺序化是通过更新最后一个 12 bytes 来完成的。 GUID 的值转换为从系统日期时间生成的值。

在初始版本中,GetUTCDate() SQL Server 系统函数用于获取日期时间并将其转换为刻度,然后用于创建 guid 的连续 12 字节。我的方法是使用 .NET Framework DateTime.UtcNow.Ticks 属性转换为最后 12 个字节,但我注意到 Ticks 属性没有更新为预期,这表明系统时间函数没有像我预期的那样频繁调用。

为了测试这一点,我创建了一个返回刻度的简单方法:

[SqlFunction()]
public static long GetTicks()
{
  return DateTime.UtcNow.Ticks;
}

此方法的程序集随后被创建为 ASSEMBLY SQL Server 中的对象;请注意,CLR 集成已启用,并且程序集是在 UNSAFE 模式下创建的,因此允许访问 OS APIs 。然后,我在 SQL Server 中为这个 CLR 函数创建了一个函数:

CREATE FUNCTION GetTicks() RETURNS BIGINT 
AS EXTERNAL NAME Sequential.Generator.GetTicks; 
GO

然后我执行了以下两条语句,结果如下所示

SELECT [dbo].[GetTicks]();
SELECT [dbo].[GetTicks]();

结果是:

enter image description here

这表明对 CLR 函数的连续调用返回了相同的 TICKS 值。这意味着未执行底层操作系统调用来为 GetTicks() 函数的每次调用返回刻度。然后,我通过创建一个简单的表来执行第二次测试,如下所示:

CREATE TABLE #tmp1(
     [NUM] [INT] NULL,
     [TICKS] BIGINT
);

我用 300 行填充表,然后执行以下语句:

UPDATE #tmp1
SET [ticks] = [Play].[dbo].[GetTicks]();

然后,我测量了连续行的 [ticks] 值之间的差异,所有 300 行中只有一次值发生变化(在 Excel 中完成):

enter image description here

这再次表明,每次调用 CLR 集成函数 GetTicks() 时,都不会调用框架 DateTime.UtcNow.Ticks 属性。

  1. 有没有办法启用 CLR 集成,以便每次从 SQL Server 调用函数时都可以执行对底层操作系统的调用?

  2. 对于 SQL Server 而言,SQL CLR 集成比 GetUTCDATE() T-SQL 函数更频繁地从操作系统检索系统时间是一种不明智的方法吗?

谢谢!

(*) 注意,顺序 GUIDS 是必需的,我只是想最大限度地提高它们生成方式的性能。此外,NEWSEQUENTIALID()由于隐私问题,该功能 Not Acceptable 。

最佳答案

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 集成”功能处于“启用”或“禁用”状态。除此之外没有任何配置。正如上面直接提到的,这是 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是如何被调用来生成值的? 插入触发器?

如果性能是一个问题,则将 PK 设置为 INTBIGINT 并使用 IDENTITY。保留 GUID 字段,但通过向其添加非聚集索引使其成为“备用键”。应用代码可以使用 GUID 值,但 JOIN 等应使用 INT/BIGINT PK。

但是,就获取更频繁更改的 DateTime.UtcNow.Ticks 值而言,从 Windows 8/Windows Server 2012 开始,有一个新的操作系统函数 GetSystemTimePreciseAsFileTime ,这将适用于此。通常我会添加“不幸的是,它要求将程序集标记为不安全”,因为除非绝对必要,否则应该避免这样做,但您已经提到将程序集标记为UNSAFE,因此这不会对那个。

为了使用这个新功能,只需从这里获取代码:

High Resolution Clock in C#

然后替换:

DateTime.UtcNow.Ticks

与:

HighResolutionDateTime.UtcNow.Ticks

!! 请注意:SQL Server 不保证函数被调用的次数。这意味着,即使极不可能,对此函数(或任何函数,包括 NEWID())的多次调用也有可能返回相同的值,即使具体情况如此声明为 IsDeterministic = false。但是,这也适用于 T-SQL UDF,因此如果进程当前正在使用 T-SQL UDF,那么一旦切换为 SQLCLR 标量函数,它应该会继续工作。

关于c# - SQL Server CLR 集成未按预期调用系统时间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35344762/

相关文章:

c# - 如何获取当前用户的应用程序设置文件夹路径?

c# - Quartz.NET - 作业不运行?

c# - xna 纹理坐标

.net - Connection关闭时DataReader没有关闭,后果?

sql-server - 一个新的 BI/数据库项目 : how to take databases under version control?

c# - 相当于 Xamarin Forms 的 Toast

.net - 任何人都可以向我解释 System.Xml.XmlDictionaryWriter.WriteNode(XmlReader, bool) 方法的 bool 参数的含义是什么?

c# - 如何在内部访问修饰符上实现Setter依赖注入(inject)

sql - SQL Server 2005 中的 "select * from table"与 "select colA, colB, etc. from table"有趣的行为

sql-server - 是否可以获取仅读取数据的存储过程列表?