次の方法で共有


CLR 統合アーキテクチャのパフォーマンス

適用対象: SQL Server Azure SQL Managed Instance

この記事では、SQL Server と Microsoft .NET Framework 共通言語ランタイム (CLR) の統合のパフォーマンスを向上させる設計上の選択肢について説明します。

コンパイル プロセス

SQL 式のコンパイル中に、マネージド ルーチンへの参照が検出されると、Microsoft Intermediate Language (MSIL) スタブが生成されます。 このスタブには、SQL Server から CLR にルーチン パラメーターをマーシャリングし、関数を呼び出して結果を返すコードが含まれています。 この "グルー" (接着剤) コードは、パラメーターの型とパラメーターの方向 (入力、出力、または参照) に基づいています。

接着コードにより、型固有の最適化が可能になり、NULL 値の許容、ファセットの制約、値による制約、標準の例外処理など、SQL Server セマンティクスの効率的な適用が保証されます。 引数に真数型を使用するコードを生成することで、複数の呼び出しにまたがる型の強制またはラッパー オブジェクト作成によるコストを回避 ("ボックス化") できます。

生成されたスタブはネイティブ コードにコンパイルされ、CLR の Just-In-Time (JIT) コンパイル サービスを使用して、SQL Server が実行される特定のハードウェア アーキテクチャ用に最適化されます。 JIT サービスはメソッド レベルで呼び出され、SQL Server ホスティング環境で、SQL Server と CLR の両方の実行にまたがる 1 つのコンパイル ユニットを作成できます。 スタブをコンパイルすると、コンパイルされた関数のポインターが関数の実行時の実装になります。 このコード生成アプローチにより、実行時のリフレクションまたはメタデータ アクセスに関連する追加の呼び出しコストが発生しないようにします。

SQL Server と CLR の間の高速遷移

コンパイル処理の結果、実行時にネイティブ コードから呼び出すことのできる関数ポインターが生成されます。 スカラー値のユーザー定義関数の場合、この関数の呼び出しは行ごとに行われます。 SQL Server と CLR の間の移行コストを最小限に抑えるために、マネージド呼び出しを含むステートメントには、ターゲット アプリケーション ドメインを識別するためのスタートアップ ステップがあります。 この識別処理により、行ごとの切り替えコストを抑えます。

パフォーマンスに関する考慮事項

次のセクションでは、SQL Server での CLR 統合に固有のパフォーマンスに関する考慮事項をまとめます。 詳細については、「 SQL Server 2005 での CLR 統合の使用」を参照してください。 マネージド コードのパフォーマンスの詳細については、「 .NET アプリケーションのパフォーマンスとスケーラビリティの向上を参照してください。

ユーザー定義関数

CLR 関数は、Transact-SQL ユーザー定義関数よりも高速な呼び出しパスを利用できます。 さらに、マネージド コードには、手続き型コード、計算、および文字列操作の観点から、Transact-SQL よりも決定的なパフォーマンス上の利点があります。 コンピューティング集中型であり、データ アクセスを実行しない CLR 関数は、マネージド コードでより適切に記述されます。 ただし、Transact-SQL 関数は CLR 統合よりも効率的にデータ アクセスを実行します。

ユーザー定義集計

マネージド コードを使用すると、カーソル ベースの集計よりも大幅に優れたパフォーマンスを発揮できます。 一般に、マネージド コードの実行速度は、組み込みの SQL Server 集計関数よりも若干遅くなります。 ネイティブの組み込み集計関数が存在する場合は、その関数を使用することをお勧めします。 必要な集計がネイティブにサポートされていない場合は、パフォーマンス上の理由から、カーソルベースの実装に対する CLR ユーザー定義集計を検討してください。

ストリーミング テーブル値関数

関数を呼び出した結果として、テーブルを返す必要性が生じる場合がよくあります。 たとえば、インポート操作の一環としてファイルから表形式のデータを読み取る場合や、コンマ区切りの値をリレーショナル表現に変換する場合などです。 一般的に、このような作業を実現するには、テーブルを呼び出し元で使用する前に、結果テーブルを具体化して値を格納する必要があります。 CLR を SQL Server に統合すると、ストリーミング テーブル値関数 (STVF) と呼ばれる新しい拡張メカニズムが導入されます。 マネージド STVF は、同様の拡張ストアド プロシージャを実装した場合に比べて、優れたパフォーマンスを発揮します。

STVF は、IEnumerable インターフェイスを返すマネージド関数です。 IEnumerable には STVF が返した結果セットの中を移動するメソッドがあります。 STVF を呼び出して返される IEnumerable は、クエリ プランに直接接続されます。 クエリ プランで行のフェッチが必要になると、IEnumerable のメソッドが呼び出されます。 このような反復的なモデルにより、テーブル全体に値が格納されるまで待たなくても、最初の行が生成された直後から結果を使用できます。 関数の呼び出しに伴うメモリの消費を大幅に抑えることもできます。

配列とカーソル

Transact-SQL カーソルが配列としてより簡単に表現されるデータを走査する必要がある場合、マネージド コードを使用してパフォーマンスを大幅に向上させることができます。

文字列データ

varchar などの SQL Server 文字データは、マネージド関数の SqlString 型または SqlChars 型にすることができます。 SqlString 変数は値全体のインスタンスをメモリに作成します。 SqlChars 変数には、ストリーミング インターフェイスが用意されており、これを使用すると、値全体のインスタンスをメモリに作成しないことでパフォーマンスおよびスケーラビリティを高めることができます。 これは、ラージ オブジェクト (LOB) データにとって重要になります。 また、SqlXml.CreateReader() が返すストリーミング インターフェイスを経由すると、サーバーの XML データにアクセスできます。

CLR と拡張ストアド プロシージャ

Microsoft.SqlServer.Serverアプリケーション プログラミング インターフェイス (API) を使用すると、拡張ストアド プロシージャで使用される Open Data Services (ODS) API よりも、マネージド プロシージャが結果セットをクライアントに送り返すパフォーマンスが向上します。 さらに、System.Data.SqlServer API は、SQL Server 2005 (9.x) で導入された xmlvarchar(max)nvarchar(max)varbinary(max)などのデータ型をサポートしていますが、ODS API は新しいデータ型をサポートするように拡張されていません。

マネージド コードを使用すると、SQL Server はメモリ、スレッド、同期などのリソースの使用を管理します。 これは、これらのリソースを公開するマネージド API が SQL Server リソース マネージャーの上に実装されるためです。 逆に、SQL Server には、拡張ストアド プロシージャのリソースの使用状況を表示または制御することはできません。 たとえば、拡張ストアド プロシージャで消費される CPU またはメモリ リソースが多すぎる場合、SQL Server でこれを検出または制御する方法はありません。 ただし、マネージド コードを使用すると、SQL Server は特定のスレッドが長期間生成されていないことを検出し、他の作業をスケジュールできるようにタスクを強制的に生成できます。 そのため、マネージド コードを使用すると、スケーラビリティとシステム リソースの使用状況が向上します。

マネージド コードでは、実行環境を維持し、セキュリティ チェックを実行するために必要な追加のオーバーヘッドが発生する場合があります。 これは、たとえば、SQL Server 内で実行していて、マネージド コードからネイティブ コードへの多数の移行が必要な場合です (SQL Server は、ネイティブ コードに移行するときにスレッド固有の設定に対して追加のメンテナンスを行う必要があるため)。 そのため、拡張ストアド プロシージャは、マネージド コードとネイティブ コードの間で頻繁に切り替わる場合に備えて、SQL Server 内で実行されているマネージド コードを大幅に上回る可能性があります。

Note

この機能は非推奨であるため、新しい拡張ストアド プロシージャを開発しないでください。

ユーザー定義型のネイティブ シリアル化

UDT (ユーザー定義型) は、スカラー型システムの拡張方式として設計されています。 SQL Server は、 Format.Nativeと呼ばれる UDT のシリアル化形式を実装します。 コンパイルのとき、型の定義に合わせてカスタマイズされた MSIL を生成するために型の構造を検査します。

ネイティブ シリアル化は、SQL Server の既定の実装です。 ユーザー定義のシリアル化を行うと、型の作成者がシリアル化のために定義したメソッドが呼び出されます。 最高のパフォーマンスを得るには、Format.Native シリアル化をできる限り使用してください。

同等の UDT の正規化

UDT の並べ替え、比較などのリレーショナル操作で、値のバイナリ表現を直接操作します。 これを行うには、ディスクに UDT の状態を正規化した (バイナリ順にした) 表現を格納します。

正規化には、次の 2 つの利点があります。

  • 型インスタンスの構築とメソッド呼び出しのオーバーヘッドを回避することで、比較操作のコストを大幅に削減します

  • UDT のバイナリ ドメインが作成され、型の値に対するヒストグラム、インデックス、およびヒストグラムの構築が可能になります。

そのため、正規化された UDT は、メソッド呼び出しを伴わない操作のネイティブ組み込み型と同様のパフォーマンス プロファイルを持ちます。

スケーラブルなメモリ使用量

マネージド ガベージ コレクションを SQL Server で適切に実行してスケーリングするには、大規模な単一の割り当てを避けてください。 サイズが 88 KB (KB) を超える割り当てはラージ オブジェクト ヒープに配置されます。これにより、ガベージ コレクションが実行され、多数の小さな割り当てよりもスケールが悪くなります。 たとえば、大きな多次元配列を割り当てる必要がある場合は、ジャグ (分散) 配列を割り当てる方が適切です。