CLR 統合アーキテクチャ - CLR でホストされる環境
適用対象:SQL ServerAzure SQL Managed Instance
SQL Server と .NET Framework 共通言語ランタイム (CLR) の統合により、データベース プログラマは C#、Visual Basic .NET、Visual C++ などの言語を使用できます。 プログラマがこれらの言語を使用して記述できるビジネス ロジックには、関数、ストアド プロシージャ、トリガー、データ型、集計などがあります。
CLR には、ガベージ コレクションメモリ、プリエンプティブ スレッド、メタデータ サービス (型リフレクション)、コード検証可能性、およびコード アクセス セキュリティが備えられています。 CLR では、クラスの検索と読み込み、メモリ内でのインスタンスのレイアウト、メソッド呼び出しの解決、ネイティブ コードの生成、セキュリティの設定、およびランタイム コンテキスト境界の設定にメタデータが使用されます。
CLR と SQL Server は、メモリ、スレッド、および同期を処理する方法でランタイム環境と異なります。 この記事では、すべてのシステム リソースが一様に管理されるように、これら 2 つの実行時間を統合する方法について説明します。 この記事では、CLR コード アクセス セキュリティ (CAS) と SQL Server セキュリティを統合して、ユーザー コードの信頼性とセキュリティで保護された実行環境を提供する方法についても説明します。
CLR アーキテクチャの基本的な概念
.NET Framework では、プログラマは高級言語を使ってクラスを実装し、そのクラスの構造 (クラスのフィールドやプロパティなど) とメソッドを定義します。 このようなメソッドの一部は、静的関数にすることができます。 プログラムのコンパイルでは、共通中間言語 (CIL) のコンパイル済みコードを含むアセンブリと呼ばれるファイルと、依存アセンブリへのすべての参照を含むマニフェストが生成されます。
Note
アセンブリは CLR アーキテクチャの不可欠な要素です。 これらは、.NET Framework でのアプリケーション コードのパッケージ化、展開、およびバージョン管理の単位です。 アセンブリを使用して、データベース内部にアプリケーション コードを配置し、完全なデータベース アプリケーションの管理、バックアップ、および復元を行うための統一された方法を提供できます。
アセンブリ マニフェストには、アセンブリに関するメタデータが含まれており、プログラムで定義されているすべての構造体、フィールド、プロパティ、クラス、継承関係、関数、およびメソッドの情報が記述されています。 マニフェストでは、アセンブリ ID の確立、アセンブリの実装を構成するファイルの指定、アセンブリを構成する型やリソースの指定、他のアセンブリに対するコンパイル時の依存関係の列挙、およびアセンブリを正しく実行するために必要な権限セットの指定を行います。 この情報を実行時に使用して、参照の解決、バージョン バインド ポリシーの設定、および読み込まれたアセンブリの整合性の検証が行われます。
.NET Framework では、アプリケーションがメタデータに取り込む可能性のある追加情報を使用してクラス、プロパティ、関数、メソッドに注釈を付けるためのカスタム属性がサポートされています。 すべての .NET Framework コンパイラでは、このような注釈を解釈せずに使用し、アセンブリ メタデータとして格納します。 このような注釈は、他のメタデータと同じ方法で調べることができます。
マネージド コードは、オペレーティング システムによって直接実行されるのではなく、CLR で実行される CIL です。 マネージド コード アプリケーションは、自動ガベージ コレクション、ランタイム型チェック、セキュリティ サポートなどの CLR サービスを使用します。 これらのサービスは、プラットフォームや言語に依存しない統一的な動作を、マネージド コード アプリケーションに提供するのに役立ちます。
CLR 統合の設計目標
ユーザー コードが SQL Server の CLR ホスト環境 (CLR 統合と呼ばれます) 内で実行される場合、次の設計目標が適用されます。
信頼性 (安全性)
ユーザー の応答を要求するメッセージ ボックスをポップしたり、プロセスを終了したりするなど、データベース エンジン プロセスの整合性を損なう操作の実行をユーザー コードに許可しないでください。 ユーザー コードは、データベース エンジンのメモリ バッファーまたは内部データ構造を上書きすることはできません。
スケーラビリティ
SQL Server と CLR には、スケジュールとメモリ管理のための異なる内部モデルがあります。 SQL Server では、スレッドが定期的に、またはロックまたは I/O を待機しているときに、スレッドが自発的に実行を生成する協調的な非プリエンプティブ スレッド モデルがサポートされています。 CLR では、プリエンプティブなスレッド モデルがサポートされます。 SQL Server 内で実行されているユーザー コードがオペレーティング システムのスレッド プリミティブを直接呼び出すことができる場合、SQL Server タスク スケジューラにうまく統合されないため、システムのスケーラビリティが低下する可能性があります。 CLR は仮想メモリと物理メモリを区別しませんが、SQL Server は物理メモリを直接管理し、構成可能な制限内で物理メモリを使用する必要があります。
このようにスレッド処理、スケジュール設定、およびメモリ管理のモデルが異なるため、数千の同時実行ユーザー セッションをサポートするまで規模が拡大された RDBMS (リレーショナル データベース管理システム) では統合が課題になります。 このアーキテクチャでは、スレッド、メモリ、および同期プリミティブを直接使用するためのアプリケーション プログラミング インターフェイス (API) を呼び出すユーザー コードによって、システムのスケーラビリティが損なわれないようにする必要があります。
セキュリティ
データベースで実行されているユーザー コードは、テーブルや列などのデータベース オブジェクトにアクセスするときに、SQL Server の認証と承認の規則に従う必要があります。 また、データベース管理者は、データベースで実行しているユーザー コードからファイルやネットワーク アクセスなどのオペレーティング システム リソースへのアクセスを制御できる必要があります。 この方法は、(Transact-SQL などの管理されていない言語とは異なり) マネージド プログラミング言語がこのようなリソースにアクセスするための API を提供するため、重要になります。 システムは、ユーザー コードがデータベース エンジン プロセス外のコンピューター リソースにアクセスするための安全な方法を提供する必要があります。 詳細については、CLR 統合セキュリティ
パフォーマンス
データベース エンジンで実行されるマネージド ユーザー コードには、サーバーの外部で実行されるのと同じコードと同等の計算パフォーマンスが必要です。 マネージド ユーザー コードからのデータベース アクセスは、ネイティブ Transact-SQL ほど高速ではありません。 詳細については、「CLR 統合アーキテクチャのパフォーマンス」を参照してください。
CLR サービス
CLR には、CLR と SQL Server の統合の設計目標を達成するために役立ついくつかのサービスが用意されています。
タイプセーフ検証
タイプ セーフなコードとは、メモリ構造にアクセスする際に適切に定義された方法のみを使用するコードのことです。 たとえば、有効なオブジェクト参照を例として考えると、タイプ セーフなコードでは、実際のフィールド メンバーに対応してメモリの固定オフセット位置にアクセスできます。 ただし、オブジェクトに属するメモリの範囲内または範囲外の任意のオフセットでコードがメモリにアクセスする場合、型セーフではありません。 アセンブリが CLR に読み込まれると、Just-In-Time (JIT) コンパイルを使用して CIL がコンパイルされる前に、ランタイムはコードを調べてその型安全性を判断する検証フェーズを実行します。 この検証に正常に合格するコードを、検証可能なタイプ セーフなコードと呼びます。
アプリケーション ドメイン
CLR では、マネージド コード アセンブリを読み込み、実行できるホスト プロセス内の実行領域として、アプリケーション ドメインの概念がサポートされます。 アプリケーション ドメインの境界でアセンブリどうしが分離されます。 アセンブリは、静的変数やデータ メンバーの可視性、およびコードを動的に呼び出す機能に関して分離されます。 また、アプリケーション ドメインはコードのロードとアンロード用のメカニズムでもあります。 アプリケーション ドメインをアンロードしないと、コードをメモリからアンロードできません。 詳細については、「 アプリケーション ドメインと CLR Integration Security」を参照してください。
コード アクセス セキュリティ (CAS)
CLR セキュリティ システムには、マネージド コードに権限を割り当てて、そのコードで実行できる操作の種類を制御する方法が用意されています。 コード アクセス権限は、コード ID (アセンブリの署名やコードの作成元など) に基づいて割り当てられます。
CLR では、コンピューター管理者が設定できるコンピューター全体のポリシーが規定されます。 このポリシーでは、コンピューターで実行される任意のマネージド コードに許可される権限が定義されます。 さらに、SQL Server などのホストがマネージド コードに対する追加の制限を指定するために使用できるホスト レベルのセキュリティ ポリシーがあります。
.NET Framework のマネージド API がコード アクセス許可によって保護されているリソースに対する操作を公開する場合、API はリソースにアクセスする前にそのアクセス許可を要求します。 この要求により、CLR セキュリティ システムが呼び出し履歴内のすべての単位のコード (アセンブリ) を包括的にチェックします。 リソースへのアクセスは、呼び出しチェーン全体にアクセス許可がある場合にのみ付与されます。
Reflection.Emit
API を使用してマネージド コードを動的に生成する機能は、SQL Server の CLR でホストされる環境内ではサポートされていません。 このようなコードには、実行する CAS アクセス許可がないため、実行時に失敗します。 詳細については、「CLR 統合コード アクセス セキュリティ」を参照してください。
ホスト保護属性 (HPA)
CLR には、.NET Framework の一部であるマネージド API に、CLR のホストにとって関心のある特定の属性に注釈を付けるメカニズムが用意されています。 次に、このような属性の例を示します。
SharedState
:API が共有状態 (静的クラス フィールドなど) を作成または管理する機能を公開するかどうかを示します。Synchronization
: API がスレッド間の同期を実行する機能を公開するかどうかを示します。ExternalProcessMgmt
: API がホスト プロセスを制御する方法を公開するかどうかを示します。
これらの属性を指定すると、ホストは、ホスト環境で禁止する必要がある、SharedState
属性などの HPA の一覧を指定できます。 この場合、CLR では禁止一覧の HPA で注釈が付けられている API がユーザー コードから呼び出されることを拒否します。 詳細については、「ホスト保護属性と CLR 統合プログラミング」を参照してください。
SQL Server と CLR の連携のしくみ
このセクションでは、SQL Server と CLR のスレッド、スケジュール、同期、およびメモリ管理モデルを SQL Server で統合する方法について説明します。 特に、スケーラビリティ、信頼性、およびセキュリティの目標に照らし合わせて、この統合について解説します。 SQL Server は、基本的に、SQL Server 内でホストされている CLR のオペレーティング システムとして機能します。 CLR は、スレッド、スケジュール、同期、およびメモリ管理のために SQL Server によって実装される低レベルルーチンを呼び出します。 これらのルーチンは、SQL Server エンジンの残りの部分で使用されるのと同じプリミティブです。 このアプローチを使用すると、スケーラビリティ、信頼性、およびセキュリティに関するいくつかの利点が得られます。
スケーラビリティ : 一般的なスレッド処理、スケジュール設定、および同期
CLR は、ユーザー コードの実行と独自の内部使用の両方で、スレッドを作成するために SQL Server API を呼び出します。 複数のスレッド間で同期するために、CLR は SQL Server 同期オブジェクトを呼び出します。 この方法では、スレッドが同期オブジェクトを待機しているときに、SQL Server スケジューラで他のタスクをスケジュールできます。 たとえば、CLR からガベージ コレクションが開始されると、CLR のすべてのスレッドがガベージ コレクションの終了を待機します。 CLR スレッドと、待機している同期オブジェクトは SQL Server スケジューラで認識されるため、SQL Server では、CLR を含まない他のデータベース タスクを実行しているスレッドをスケジュールできます。 これにより、SQL Server は、CLR 同期オブジェクトによって取得されたロックを含むデッドロックを検出し、デッドロックの削除に従来の手法を採用することもできます。
マネージド コードは、SQL Server でプリエンプティブに実行されます。 SQL Server スケジューラには、長時間生成されていないスレッドを検出して停止する機能があります。 CLR スレッドを SQL Server スレッドにフックする機能は、SQL Server スケジューラが CLR 内の "ランナウェイ" スレッドを識別し、その優先順位を管理できることを意味します。 このようなランナウェイ スレッドは中断され、キューに戻されます。 ランナウェイ スレッドとして繰り返し識別されるスレッドは、他の実行中のワーカーが実行できるように、特定の期間実行できません。
実行時間の長いマネージド コードが自動的に生成される状況と、そうでない状況があります。 次の状況では、実行時間の長いマネージド コードが自動的に生成されます。
- コードが SQL OS を呼び出す場合 (データのクエリなど)
- ガベージ コレクションをトリガーするのに十分なメモリが割り当てられている場合
- OS 関数を呼び出してコードがプリエンプティブ モードに入った場合
計算のみを含むタイト なループなど、これらのアクションを実行しないコードでは、スケジューラが自動的に生成されないため、システム内の他のワークロードを長時間待機する可能性があります。 このような状況では、.NET Framework の System.Thread.Sleep()
関数を呼び出すか、System.Thread.BeginThreadAffinity()
を使用して明示的にプリエンプティブ モードに入ることで、実行時間が長いことが予想されるコードのセクションで明示的に生成する必要があります。 次のコード例は、これらの各メソッドを使用して手動で生成する方法を示しています。
例
SOS スケジューラに手動で生成する
for (int i = 0; i < Int32.MaxValue; i++)
{
// *Code that does compute-heavy operation, and does not call into
// any OS functions.*
// Manually yield to the scheduler regularly after every few cycles.
if (i % 1000 == 0)
{
Thread.Sleep(0);
}
}
ThreadAffinity を使用してプリエンプティブに実行する
この例では、CLR コードは、BeginThreadAffinity
および EndThreadAffinity
内でプリエンプティブ モードで実行されます。
Thread.BeginThreadAffinity();
for (int i = 0; i < Int32.MaxValue; i++)
{
// *Code that does compute-heavy operation, and does not call into
// any OS functions.*
}
Thread.EndThreadAffinity();
スケーラビリティ : 一般的なメモリ管理
CLR は、メモリの割り当てと割り当て解除のために SQL Server プリミティブを呼び出します。 CLR によって使用されるメモリはシステムの合計メモリ使用量に考慮されるため、SQL Server は構成されたメモリ制限内に留まり、CLR と SQL Server がメモリを互いに競合しないようにすることができます。 SQL Server では、システム メモリが制約されている場合に CLR メモリ要求を拒否したり、他のタスクでメモリが必要な場合のメモリ使用量を減らすように CLR に依頼したりすることもできます。
信頼性 : アプリケーション ドメインと回復できない例外
.NET Framework API のマネージド コードでメモリ不足やスタック オーバーフローなどの重大な例外が発生した場合、このようなエラーから復旧し、実装の一貫性と正しいセマンティクスを確保できるわけではありません。 これらの API により、このようなエラーへの応答でスレッドを中断する例外が発生します。
SQL Server でホストされている場合、このようなスレッドの中止は次のように処理されます。CLR は、スレッドの中止が発生したアプリケーション ドメイン内の共有状態を検出します。 CLR は、同期オブジェクトの存在を確認することでこれを検出します。 アプリケーション ドメインに共有状態がある場合、アプリケーション ドメイン自体はアンロードされます。 アプリケーション ドメインをアンロードすると、そのアプリケーション ドメインで現在実行されているデータベース トランザクションが停止します。 共有状態が存在すると、このような重大な例外が例外をトリガーするセッション以外のユーザー セッションに及ぼす影響が広がる可能性があるため、SQL Server と CLR では、共有状態の可能性を減らすための手順を実行しています。 詳細については、「.NET Frameworkの
セキュリティ : 権限セット
SQL Server を使用すると、ユーザーはデータベースにデプロイされるコードの信頼性とセキュリティの要件を指定できます。 アセンブリをデータベースにアップロードする場合、アセンブリの作成者は、そのアセンブリの 3 つの権限セット (SAFE
、EXTERNAL_ACCESS
、UNSAFE
) のいずれかを指定できます。
機能 | SAFE |
EXTERNAL_ACCESS |
UNSAFE |
---|---|---|---|
Code Access Security |
実行のみ | 実行および外部リソースへのアクセス | 無制限 |
Programming model restrictions |
はい | はい | 制限事項なし |
Verifiability requirement |
はい | はい | いいえ |
Ability to call native code |
いいえ | いいえ | はい |
SAFE
は、許可されたプログラミング モデルの観点から関連する制限を持つ最も信頼性が高く、セキュリティで保護されたモードです。
SAFE
アセンブリには、実行、計算、およびローカル データベースへのアクセス権を持つ十分なアクセス許可が与えられます。
SAFE
アセンブリは、検証可能な型セーフである必要があり、アンマネージ コードを呼び出すことはできません。
UNSAFE
は、データベース管理者のみが作成できる信頼性の高いコード用です。 この信頼性の高いコードにはコード アクセス セキュリティに関する制限がなく、アンマネージ (ネイティブ) コードを呼び出すことができます。
EXTERNAL_ACCESS
は中間セキュリティ オプションを提供します。これにより、コードはデータベースの外部のリソースにアクセスできますが、SAFE
の信頼性が保証されます。
SQL Server では、ホスト レベルの CAS ポリシー レイヤーを使用して、SQL Server カタログに格納されているアクセス許可セットに基づいて、3 つのアクセス許可セットのいずれかを付与するホスト ポリシーを設定します。 データベース内部で実行するマネージド コードには、これらのコード アクセス権限セットのうちのいずれかが必ず許可されます。
プログラミング モデルの制限事項
SQL Server のマネージド コードのプログラミング モデルには、関数、プロシージャ、型の記述が含まれます。通常、複数の呼び出しで保持されている状態の使用や、複数のユーザー セッション間での状態の共有は必要ありません。 さらに、前述のように、共有状態が存在すると、アプリケーションのスケーラビリティと信頼性に影響を与える重大な例外が発生する可能性があります。
これらの考慮事項を考慮すると、SQL Server で使用されるクラスの静的変数と静的データ メンバーを使用しないことをお勧めします。
SAFE
アセンブリと EXTERNAL_ACCESS
アセンブリの場合、SQL Server は CREATE ASSEMBLY
時間にアセンブリのメタデータを調べ、静的データ メンバーと変数の使用が見つかると、そのようなアセンブリの作成に失敗します。
SQL Server では、SharedState
、Synchronization
、およびホスト保護属性で注釈が付けられた .NET Framework API ExternalProcessMgmt
呼び出しも禁止されます。 これにより、SAFE
アセンブリと EXTERNAL_ACCESS
アセンブリが、状態の共有、同期の実行、および SQL Server プロセスの整合性に影響を与える API を呼び出すのを防ぐことができます。 詳細については、CLR 統合プログラミング モデルの制限
関連コンテンツ
- CLR 統合セキュリティ の
- CLR 統合アーキテクチャのパフォーマンス