次の方法で共有


CLR 統合のアーキテクチャ - CLR ホスト環境

適用対象: SQL Server Azure SQL Managed Instance

SQL Server と .NET Framework 共通言語ランタイム (CLR) の統合により、データベース プログラマは Visual C#、Visual Basic .NET、Visual C++ などの言語を使用できます。 プログラマがこれらの言語を使用して記述できるビジネス ロジックには、関数、ストアド プロシージャ、トリガー、データ型、集計などがあります。

CLR には、ガベージ コレクションメモリ、プリエンプティブ スレッド、メタデータ サービス (型リフレクション)、コード検証可能性、およびコード アクセス セキュリティが備えられています。 CLR では、クラスの検索と読み込み、メモリ内でのインスタンスのレイアウト、メソッド呼び出しの解決、ネイティブ コードの生成、セキュリティの設定、およびランタイム コンテキスト境界の設定にメタデータが使用されます。

CLR と SQL Server は、メモリ、スレッド、および同期を処理する方法でランタイム環境と異なります。 この記事では、すべてのシステム リソースが一様に管理されるように、これら 2 つの実行時間を統合する方法について説明します。 この記事では、CLR コード アクセス セキュリティ (CAS) と SQL Server セキュリティを統合して、ユーザー コードの信頼性とセキュリティで保護された実行環境を提供する方法についても説明します。

CLR アーキテクチャの基本概念

.NET Framework では、プログラマは高級言語を使ってクラスを実装し、そのクラスの構造 (クラスのフィールドやプロパティなど) とメソッドを定義します。 このようなメソッドの一部は、静的関数にすることができます。 プログラムのコンパイルでは、Microsoft Intermediate Language (MSIL) のコンパイル済みコードを含むアセンブリと呼ばれるファイルと、依存アセンブリへのすべての参照を含むマニフェストが生成されます。

Note

アセンブリは CLR アーキテクチャの不可欠な要素です。 アセンブリは、.NET Framework のアプリケーション コードのパッケージ化、配置、およびバージョン管理の単位になります。 アセンブリを使用して、データベース内部にアプリケーション コードを配置し、完全なデータベース アプリケーションの管理、バックアップ、および復元を行うための統一された方法を提供できます。

アセンブリ マニフェストには、アセンブリに関するメタデータが含まれており、プログラムで定義されているすべての構造体、フィールド、プロパティ、クラス、継承関係、関数、およびメソッドの情報が記述されています。 マニフェストでは、アセンブリ ID の確立、アセンブリの実装を構成するファイルの指定、アセンブリを構成する型やリソースの指定、他のアセンブリに対するコンパイル時の依存関係の列挙、およびアセンブリを正しく実行するために必要な権限セットの指定を行います。 この情報を実行時に使用して、参照の解決、バージョン バインド ポリシーの設定、および読み込まれたアセンブリの整合性の検証が行われます。

.NET Framework では、アプリケーションがメタデータでキャプチャできる追加情報により、クラス、プロパティ、関数、およびメソッドに注釈を付けるためのカスタム属性がサポートされます。 すべての .NET Framework コンパイラでは、このような注釈を解釈せずに使用し、アセンブリ メタデータとして格納します。 このような注釈は、他のメタデータと同じ方法で調べることができます。

マネージド コードは、オペレーティング システムが直接実行するのではなく、CLR で MSIL として実行されます。 マネージド コード アプリケーションは、自動ガベージ コレクション、ランタイム型チェック、セキュリティ サポートなどの 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 に読み込むと、JIT (Just-In-Time) コンパイルを使用して MSIL にコンパイルされる前に、ランタイムによって、コードのタイプ セーフティを判断するためにそのコードを調べる検証フェーズが実行されます。 この検証に正常に合格するコードを、検証可能なタイプ セーフなコードと呼びます。

アプリケーション ドメイン

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 がユーザー コードから呼び出されることを拒否します。 詳細については、「 Host Protection 属性と 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() を使用してプリエンティブ モードに明示的に入ることで、明示的に生成する必要があります。 次のコード例は、これらの各メソッドを使用して手動で生成する方法を示しています。

// Example 1: Manually yield to SOS scheduler.
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);
 }
}
// Example 2: Use ThreadAffinity to run preemptively.
// Within BeginThreadAffinity/EndThreadAffinity the CLR code runs in preemptive mode.
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 の実装に対して一貫性のある正しいセマンティクスを保証できるとは限りません。 これらの API により、このようなエラーへの応答でスレッドを中断する例外が発生します。

SQL Server でホストされている場合、このようなスレッドの中止は次のように処理されます。CLR は、スレッドの中止が発生したアプリケーション ドメイン内の共有状態を検出します。 CLR は、同期オブジェクトの存在を確認することでこれを検出します。 アプリケーション ドメインに共有状態が存在する場合は、アプリケーション ドメイン自体がアンロードされます。 アプリケーション ドメインをアンロードすると、そのアプリケーション ドメインで現在実行されているデータベース トランザクションが停止します。 共有状態が存在すると、このような重大な例外が例外をトリガーするセッション以外のユーザー セッションへの影響を拡大する可能性があるため、SQL Server と CLR は、共有状態の可能性を減らすための手順を実行しました。 詳細については、 .NET Framework のドキュメントを参照してください。

セキュリティ : 権限セット

SQL Server を使用すると、ユーザーはデータベースにデプロイされるコードの信頼性とセキュリティの要件を指定できます。 アセンブリがデータベースにアップロードされると、アセンブリの作成者は、そのアセンブリの 3 つの権限セット (SAFE、EXTERNAL_ACCESS、UNSAFE) のいずれかを指定できます。

機能 SAFE EXTERNAL_ACCESS UNSAFE
コード アクセス セキュリティ 実行のみ 実行および外部リソースへのアクセス 無制限
プログラミング モデルの制限事項 はい はい 制限事項なし
検証可能性の要件 はい はい いいえ
ネイティブ コードを呼び出す機能 いいえ 番号 はい

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 では、 SharedStateSynchronization、および ExternalProcessMgmt ホスト保護属性で注釈が付けられた .NET Framework API の呼び出しも禁止されます。 これにより、SAFE アセンブリと EXTERNAL_ACCESS アセンブリが、状態の共有、同期の実行、および SQL Server プロセスの整合性に影響を与える API を呼び出すのを防ぐことができます。 詳細については、「 CLR 統合プログラミング モデルの制限」を参照してください。

参照

CLR 統合のセキュリティ
CLR 統合のパフォーマンス