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

適用対象:SQL ServerAzure SQL Managed Instance

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

コンパイル処理

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

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

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

SQL Server と CLR の高速切り替え

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

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

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

ユーザー定義関数

CLR 関数は、Transact-SQL ユーザー定義関数よりも高速な呼び出しパスを利用できます。 さらに、マネージド コードには、手続き型コード、計算、文字列操作の観点から、Transact-SQL よりも決定的なパフォーマンス上の利点があります。 計算中心の CLR 関数およびデータ アクセスを行わない 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 (ラージ オブジェクト) データにとって重要です。 さらに、サーバー XML データには、 SqlXml.CreateReader() によって返されるストリーミング インターフェイスを介してアクセスできます。

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

マネージド プロシージャから結果セットをクライアントに返す Microsoft.SqlServer.Server API (アプリケーション プログラミング インターフェイス) は、拡張ストアド プロシージャにより使用される 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内で実行されているマネージ コードを大幅に上回る可能性があります。

注意

この機能の使用は非推奨とされるため、拡張ストアド プロシージャを新規作成しないことをお勧めします。

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

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

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

同等の UDT の正規化

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

正規化には 2 つの利点があります。1 つは、型のインスタンスの作成やメソッド呼び出しのオーバヘッドが発生しないようにすることで比較操作のコストが大幅に抑えられることです。もう 1 つは、UDT のバイナリ領域が作成され、ヒストグラム、インデックス、およびその型の値のヒストグラムが作成できるようになることです。 つまり、メソッド呼び出しを伴わない操作では、正規化した UDT はネイティブの組み込み型と変わらないパフォーマンスを発揮します。

スケーラビリティを確保するメモリの使用方法

マネージド ガベージ コレクションをSQL Serverで適切に実行してスケーリングするには、大規模な単一の割り当てを避けてください。 88 KB を超える割り当てはラージ オブジェクト ヒープに配置されます。その結果、小規模の割り当てをいくつも行った場合に比べて、ガベージ コレクションのパフォーマンスやスケーラビリティが低下します。 たとえば、大きな多次元配列を割り当てる場合、ジャグ (散在した) 配列を割り当てることをお勧めします。

参照

CLR ユーザー定義型