どうやら、私のCLRアセンブリ関数がデッドロックを引き起こしていますか?


9

私たちのアプリケーションは、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
確定的スカラーCLR関数はデッドロックの一因にはなりません。もちろん、データベースを読み取るCLR関数は可能性があります。質問にはデッドロックXMLを含める必要があります。
デビッドブラウン-マイクロソフト

回答:


7

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

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

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

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

    1. 確定的なSQLCLR UDFが並列プランに参加できる場合でも、単純な組み込み関数をエミュレートするためにパフォーマンスに影響を与える可能性が高くなります。
    2. SQLCLR APIでは、は許可されていませんVARCHAR。暗黙的にすべてをに変換NVARCHARしてからVARCHAR、単純な操作のために再び戻すことに問題はありませんか?
    3. SQLCLR APIではオーバーロードが許可されないため、T-SQLやPL / SQLで異なるシグネチャを許可する関数の複数のバージョンが必要になる場合があります。
    4. オーバーロードを許可しないのと同様に、との間には大きな違いがNVARCHAR(4000)ありNVARCHAR(MAX)ます。MAXタイプ(シグニチャーにタイプが1つでもある)MAXは、シグニチャーにタイプがない限り、SQLCLR呼び出しに2倍の時間を要します(これは当てはまると思います)VARBINARY(MAX)vs VARBINARY(4000)にも当てはまります)。したがって、次のいずれかを決定する必要があります。
      • NVARCHAR(MAX)単純化されたAPI を使用するためにのみ使用し、8000バイト以下の文字列データを使用している場合にパフォーマンスに影響を与える、または
      • すべて/ほとんど/多くの文字列関数に対して2つのバリエーションを作成しMAXます。1つはタイプあり、もう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つしか使用していないことに気づきました。これは単なる概念実証、または編集されたコードですか?

1. Sqlタイプの使用に関するヒントをありがとう。今から変更します。2.ここには外力が働いており、それらを使用する必要があります。私はそれについてわくわくしていませんが、私を信じて、それは代替案よりも優れています。私の元の質問には、一見、一見すると非現実的な関数が存在し、使用されている理由についての説明が少し含まれています。
Russ Suter、

@RussSuter理解しましたre:外力。私はその決定がなされたときに知られていないかもしれないいくつかの落とし穴を指摘していました。どちらにしても、覚えているいくつかの詳細から自分のノートを見つけたり、シナリオを再現したりできません。トランザクションとアセンブリからのコードの呼び出しに関して2017年に明確に変更された何かを覚えています。悪いことに不必要な変更のように見えたので、本当にうんざりしていました。以前のバージョンでは問題ありません。質問のリンクをデッドロックXMLに投稿してください。
ソロモンルツキー

その追加情報をありがとう。XMLへのリンクは次のとおり
Russ Suter

@RussSuter T-SQLのインライン化でこれを試しましたか?デッドロックXML(1行なので簡単ではありません-すべての改行が何らかの理由で削除されました)を見ると、セッション60と78の間で一連のPAGEロックが発生しているようです。両方のセッション間で8ページがロックされています。 SPIDおよびその他の5。プロセスIDがそれぞれ異なるため、これは並列処理の問題です。これがSQLCLRに関連している場合、皮肉なことに、SQLCLRが並列処理を妨げていない可能性があります。これが、デッドロックを示す可能性があるため、単純な関数をインラインに配置してみたかどうかを尋ねた理由です。
ソロモンルツキー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.