web-dev-qa-db-ja.com

どうやら、私の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

CLRアセンブリ関数

[SqlFunction(IsDeterministic = true)]
public static string APAD(string value, string tableName, string columnName)
{
    return value?.Trim();
}

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
9
Russ Suter

SQL Serverのどのバージョンを使用していますか?

SQL Server 2017で動作が少し変わったのを覚えていますか。戻って、どこにメモしたかを見つける必要があるかどうかを確認する必要がありますが、SQLCLRオブジェクトがアクセスされているときにスキーマロックが開始されたことが原因であると思います。

私がそれを探している間、私はあなたのアプローチに関して次のことを言います:

  1. 入力パラメータ、戻り値の型には_Sql*_型を使用してください。 SqlStringではなくstringを使用する必要があります。 SqlStringはnull可能文字列(お使いの_value?_)によく似ていますが、SQL Server固有のその他の機能が組み込まれています。すべての_Sql*_タイプには、予期される.NETタイプを返すValueプロパティがあります(たとえば、_SqlString.Value_はstringを返し、_SqlInt32_はintを返し、SqlDateTimeDateTimeを返しますなど)。
  2. デッドロックが関係しているかどうかにかかわらず、まずこのアプローチ全体に反対することをお勧めします。私はこれをこう言います:

    1. 確定的なSQLCLR UDFが並列プランに参加できる場合でも、単純な組み込み関数をエミュレートするためにパフォーマンスヒットを得る可能性が高くなります。
    2. SQLCLR APIはVARCHARを許可しません。すべてを暗黙的にNVARCHARに変換してから、再びVARCHARに戻して簡単な操作を行うことはできますか?
    3. SQLCLR APIではオーバーロードが許可されないため、T-SQLやPL/SQLで異なるシグネチャを許可する関数の複数のバージョンが必要になる場合があります。
    4. オーバーロードを許可しないのと同様に、NVARCHAR(4000)NVARCHAR(MAX)の間には大きな違いがあります。MAXタイプ(シグネチャに1つでもある)は、SQLCLR呼び出しを2回行いますシグネチャにMAXタイプがない限り(これはVARBINARY(MAX)VARBINARY(4000)にも当てはまると思います)。したがって、次のいずれかを決定する必要があります。
      • NVARCHAR(MAX)のみを使用してAPIを簡略化しますが、8000バイト以下の文字列データを使用している場合はパフォーマンスに影響します。または
      • すべて/ほとんど/多くの文字列関数に対して2つのバリエーションを作成します。1つはMAX型、もう1つはありません(8000バイトを超える文字列データが出入りしないことが保証されている場合)。これは、私の SQL# ライブラリ内のほとんどの関数に対して選択したアプローチです。Trim()関数が1つ以上のMAXタイプとTrim4k()バージョン。これは、シグネチャまたは結果セットスキーマのどこにもMAXタイプを持たないものです。 「4k」バージョンは絶対に効率的です。
    5. 問題の例で与えられた機能をエミュレートするように注意していない。 LTRIMおよびRTRIMはスペースのみをトリムしますが、.NET String.Trim()は空白(少なくともスペース、タブ、および改行)をトリムします。例えば:

      _  PRINT LTRIM(RTRIM(N'      a       '));
      _
    6. また、T-SQLとC#の両方の関数で、3つの入力パラメーターのうち1つしか使用していないことに気づきました。これは単なる概念実証、または編集されたコードですか?
7
Solomon Rutzky