Rus*_*ter 9 sql-server deadlock functions sql-clr
我们的应用程序需要与 Oracle 数据库或 Microsoft SQL Server 数据库同样良好地工作。为了促进这一点,我们创建了一些 UDF 来统一我们的查询语法。例如,SQL Server 有 GETDATE() 而 Oracle 有 SYSDATE。它们执行相同的功能,但它们是不同的词。我们为两个平台编写了一个名为 NOW() 的包装器 UDF,它将相关的平台特定语法包装在一个公共函数名称中。我们还有其他这样的功能,其中一些本质上什么都不做,只是为了同质化而存在。不幸的是,这对 SQL Server 来说是有代价的。内联标量 UDF 对性能造成严重破坏并完全禁用并行性。作为替代方案,我们编写了 CLR 汇编函数来实现相同的目标。当我们将其部署到客户端时,他们开始经常遇到死锁。这个特定的客户端正在使用复制和高可用性技术,我想知道这里是否存在某种交互。我只是不明白如何引入 CLR 函数会导致这样的问题。作为参考,我已经包含了原始标量 UDF 定义以及 C# 中的替换 CLR 定义及其 SQL 声明。如果有帮助,我也可以提供死锁 XML。
原始UDF
CREATE FUNCTION [fn].[APAD]
(
@Value VARCHAR(4000)
, @tablename VARCHAR(4000) = NULL
, @columnname VARCHAR(4000) = NULL
)
RETURNS VARCHAR(4000)
WITH SCHEMABINDING
AS
BEGIN
RETURN LTRIM(RTRIM(@Value))
END
GO
Run Code Online (Sandbox Code Playgroud)
CLR 汇编函数
[SqlFunction(IsDeterministic = true)]
public static string APAD(string value, string tableName, string columnName)
{
return value?.Trim();
}
Run Code Online (Sandbox Code Playgroud)
CLR 函数的 SQL Server 声明
CREATE FUNCTION [fn].[APAD]
(
@Value NVARCHAR(4000),
@TableName NVARCHAR(4000),
@ColumnName NVARCHAR(4000)
) RETURNS NVARCHAR(4000)
AS
EXTERNAL NAME ASI.fn.APAD
GO
Run Code Online (Sandbox Code Playgroud)
您使用的是什么版本的 SQL Server?
我确实记得不久前看到 SQL Server 2017 中的行为发生了轻微变化。我将不得不回去看看我是否能找到我记录它的地方,但我认为这与访问 SQLCLR 对象时启动的模式锁有关。
当我在寻找那个时,我会就你的方法说以下几点:
Sql*
输入参数的类型,返回类型。您应该使用SqlString
而不是string
. SqlString
非常类似于可为空的字符串(您的value?
,但它具有其他内置的 SQL Server 特定功能。所有Sql*
类型都有一个Value
返回预期 .NET 类型的属性(例如,SqlString.Value
returns string
、SqlInt32
returns int
、SqlDateTime
returnsDateTime
等)。无论死锁是否相关,我都建议不要从整个方法开始。我这样说是因为:
VARCHAR
. 您是否可以将所有内容隐式转换为NVARCHAR
然后再次转换VARCHAR
为简单操作?NVARCHAR(4000)
和之间有很大的区别NVARCHAR(MAX)
:MAX
类型(在签名中甚至只有一个)使 SQLCLR 调用花费的时间是MAX
签名中没有任何类型的两倍(我相信这成立对VARBINARY(MAX)
vsVARBINARY(4000)
也是如此)。因此,您需要在以下两者之间做出决定:
NVARCHAR(MAX)
简化的 API,但在使用 8000 字节或更少的字符串数据时会降低性能,或MAX
类型,另一种没有(当你保证永远不会输入或输出超过 8000 字节的字符串数据时)。这是我为SQL#库中的大多数函数选择的方法:有一个Trim()
函数可能具有一种或多种MAX
类型,而一个Trim4k()
版本MAX
在签名或结果集架构中的任何地方都没有类型。“4k”版本绝对更高效。鉴于问题中的示例,您没有仔细模拟功能。LTRIM
并且RTRIM
只修剪空格,而 .NETString.Trim()
修剪空白(至少是空格、制表符和换行符)。例如:
PRINT LTRIM(RTRIM(N' a '));
Run Code Online (Sandbox Code Playgroud)