CLR 統合のパフォーマンス
このトピックでは、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 の両方の実行にまたがる単一のコンパイル ユニットを作成できます。 スタブをコンパイルすると、コンパイルされた関数のポインターが関数の実行時の実装になります。 このコード生成方式によって、実行時にリフレクションやメタデータのアクセスのために追加の呼び出しを行うコストを回避することができます。
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 (ラージ オブジェクト) データにとって重要です。 また、SqlXml.CreateReader()
が返すストリーミング インターフェイスを経由すると、サーバーの XML データにアクセスできます。
CLR と拡張ストアド プロシージャ
マネージド プロシージャから結果セットをクライアントに返す Microsoft.SqlServer.Server API (アプリケーション プログラミング インターフェイス) は、拡張ストアド プロシージャにより使用される ODS (オープン データ サービス) API に比べパフォーマンスに優れています。 さらに、System.Data.SqlServer API では、SQL Server 2005 (9.x) で導入された 、varchar(max)
、nvarchar(max)
、 varbinary(max)
などのxml
データ型がサポートされていますが、ODS API は新しいデータ型をサポートするように拡張されていません。
マネージド コードでは、SQL Serverはメモリ、スレッド、同期などのリソースの使用を管理します。 これは、これらのリソースを公開するマネージド API が、SQL Server リソース マネージャーの上に実装されるためです。 逆に、SQL Serverには、拡張ストアド プロシージャのリソース使用量を表示または制御する機能はありません。 たとえば、拡張ストアド プロシージャで CPU またはメモリ リソースの消費が多すぎる場合、SQL Serverを使用してこれを検出または制御する方法はありません。 ただし、マネージド コードを使用すると、SQL Serverは、特定のスレッドが長期間生成されていないことを検出し、他の作業をスケジュールできるようにタスクを強制的に生成できます。 つまり、マネージド コードを使用すると、スケーラビリティやシステム リソースの使用状況が改善されます。
マネージド コードを使用すると、実行環境の保持およびセキュリティ チェックの実施に必要なオーバーヘッドが発生することがあります。 これは、たとえば、SQL Server内で実行していて、マネージド コードからネイティブ コードへの多数の移行が必要な場合です (SQL Serverは、ネイティブ コードに移行するときにスレッド固有の設定に対して追加のメンテナンスを行う必要があるためです)。 そのため、拡張ストアド プロシージャは、マネージド コードとネイティブ コードの間で頻繁に切り替わる場合に、SQL Server内で実行されているマネージド コードを大幅に上回る可能性があります。
注意
この機能の使用は非推奨とされるため、拡張ストアド プロシージャを新規作成しないことをお勧めします。
ユーザー定義型のネイティブ シリアル化
UDT (ユーザー定義型) は、スカラー型システムの拡張方式として設計されています。 SQL Server は、 という UDT Format.Native
のシリアル化形式を実装します。 コンパイルのとき、型の定義に合わせてカスタマイズされた MSIL を生成するために型の構造を検査します。
ネイティブ シリアル化は、SQL Serverの既定の実装です。 ユーザー定義のシリアル化を行うと、型の作成者がシリアル化のために定義したメソッドが呼び出されます。 最高のパフォーマンスを得るには、Format.Native
シリアル化をできる限り使用してください。
同等の UDT の正規化
UDT の並べ替え、比較などのリレーショナル操作で、値のバイナリ表現を直接操作します。 これを行うには、ディスクに UDT の状態を正規化した (バイナリ順にした) 表現を格納します。
正規化には 2 つの利点があります。1 つは、型のインスタンスの作成やメソッド呼び出しのオーバヘッドが発生しないようにすることで比較操作のコストが大幅に抑えられることです。もう 1 つは、UDT のバイナリ領域が作成され、ヒストグラム、インデックス、およびその型の値のヒストグラムが作成できるようになることです。 つまり、メソッド呼び出しを伴わない操作では、正規化した UDT はネイティブの組み込み型と変わらないパフォーマンスを発揮します。
スケーラビリティを確保するメモリの使用方法
マネージド ガベージ コレクションをSQL Serverで適切に実行およびスケーリングするには、大きな単一の割り当てを避けてください。 88 KB を超える割り当てはラージ オブジェクト ヒープに配置されます。その結果、小規模の割り当てをいくつも行った場合に比べて、ガベージ コレクションのパフォーマンスやスケーラビリティが低下します。 たとえば、大きな多次元配列を割り当てる場合、ジャグ (散在した) 配列を割り当てることをお勧めします。