Data Provider for .NET Guide (v11)

.NET アプリケーションの設計

ここでは、.NET アプリケーションを設計するときに、システム パフォーマンスを最適化する方法を説明します。

接続の管理

アプリケーションのパフォーマンスを向上させるには、接続を適切に管理することが重要です。接続を複数回行う代わりに、1 回の接続で複数のステートメント オブジェクトを使用することで、アプリケーションのパフォーマンスを最適化します。初期接続の確立後は、データソースへの接続は行わないようにします。

接続プールを使用すると、特に、ネットワークまたは World Wide Web を介して接続するアプリケーションのパフォーマンスを大幅に向上させることができます。また、接続プールによって接続の再利用が可能になります。接続を閉じても、データベースとの物理的接続を閉じるわけではありません。アプリケーションが接続を要求すると、アクティブな接続が再利用されるので、新しい接続の作成に必要なネットワークへの I/O は発生しません。

接続は、あらかじめ割り当てておきます。まず、どの接続文字列が必要かを確認します。1 つの固有な接続文字列で、新しい接続プールが 1 つ作成されます。

一度作成された接続プールは、アクティブなプロセスが終了するか、Connection Lifetime に指定された時間が過ぎるまで破棄されません。アクティブでないプールや空のプールを維持するのに必要なシステム オーバーヘッドは、ごくわずかです。

接続とステートメントの処理は、実装前に決めておく必要があります。接続方法を慎重に管理することで、アプリケーションのパフォーマンスが向上し、メンテナンスも簡単になります。

接続の開閉

接続は、それが必要になる直前に開いてください。必要になるより早く接続を開くと、ほかのユーザーが使用できる接続の数が減り、リソースの需要が増える可能性があります。

使用可能なリソースを保持するには、接続が必要でなくなったらすぐに接続を明示的に閉じます。有効範囲外になった接続がガベージ コレクターによって暗黙的にクリーンアップされるのを待つ場合は、接続は直ちに接続プールに戻されず、実際には使用されていないリソースに関連付けられます。

finally ブロックの内側で接続を閉じます。finally ブロック内のコードは、例外が発生した場合でも必ず実行されます。これにより、接続が明示的に閉じられることが保証されます。たとえば、次のようにします。

try  
{ 
   DBConn.Open(); 
...// 必要な作業を行います 
} 
catch (Exception ex)  
{ 
   // 例外を処理します 
} 
finally 
{ 
   //  接続を閉じます 
   if (DBConn != null) 
      DBConn.Close(); 
} 

接続プールを使用している場合には、接続の開閉は不経済な操作ではありません。データ プロバイダーの Connection オブジェクトの Close() メソッドを使用すると、接続は接続プールに追加されるか戻されます。ただし、自動的に接続を閉じると、その接続に関連付けられているすべての DataReader オブジェクトが閉じられることを忘れないでください。

ステートメント キャッシングの使用

ステートメント キャッシュは、プリペアド ステートメントのグループまたは Command オブジェクトのインスタンスで、アプリケーションによって再使用が可能です。ステートメント キャッシュを使用するとアプリケーションのパフォーマンスを向上させることができます。これは、プリペアド ステートメントの動作が、そのステートメントがアプリケーションの存続期間中に何度再使用されたとしても、1 度だけ実行されるためです。

ステートメント キャッシュは物理接続に属します。実行された後、プリペアド ステートメントはステートメント キャッシュに置かれ、接続が閉じられるまで保持されます。

アプリケーションが使用する全プリペアド ステートメントをキャッシュすれば、パフォーマンスが向上するように思われます。しかし、この手法では、接続プールを使ってステートメント キャッシングを実装した場合、データベースのメモリに負担をかける結果になります。この場合、プールされた各接続がステートメント キャッシュを持ち、アプリケーションで使用される全プリペアド ステートメントを各自のキャッシュに含むことになります。これらのプールされたプリペアド ステートメントは、すべてデータベースのメモリにも保持されます。

コマンドの複数回使用

Command.Prepare メソッドを使用するかどうかによって、クエリ実行のパフォーマンスは良くも悪くも大きな影響を受けます。Command.Prepare メソッドは、基となるデータ プロバイダーに対し、パラメーター マーカーを使用するステートメントの複数回実行を最適化するように指示します。実行メソッド(ExecuteReader、ExecuteNonQuery、または ExecuteScalar)が使用されているかどうかにかかわらず、あらゆるコマンドの準備が可能であることに留意してください。

ADO.NET データ プロバイダーが、プリペアド ステートメントを含んでいるストアド プロシージャをサーバー上で作成することにより、Command.Prepare を実装する場合を考えてみましょう。ストアド プロシージャの作成には多くのオーバーヘッドを要しますが、ステートメントは複数回実行することができます。ストアド プロシージャの作成はパフォーマンスに悪影響を与えますが、プロシージャの作成時にクエリが解析され、最適化パスが保管されるため、作成したステートメントの実行は最小化されます。同じステートメントを複数回実行するアプリケーションは、Command.Prepare を呼び出してからそのコマンドを複数回実行することで、大きなメリットを得ることができます。

しかし、1 回しか実行しないステートメントに対して Command.Prepare を使用すると、不要なオーバーヘッドが生じる結果になります。さらに、大きな単一の実行クエリ バッチに対して Command.Prepare を使用するアプリケーションではパフォーマンスが低下します。同様に、常に Command.Prepare を使用するか、まったく Command.Prepare を使用しないアプリケーションは、プリペアド ステートメントとアンプリペアド ステートメントを論理的に組み合わせて使用するアプリケーションとは同じように機能しません。

ネイティブの管理プロバイダーの使用

アンマネージ コード、つまり .NET 環境外のコードへのブリッジは、パフォーマンスを低下させます。マネージ コードからアンマネージ コードを呼び出すと、データ プロバイダーの実行速度はマネージ コードのみのデータ プロバイダーよりも著しく遅くなります。このようなパフォーマンスが大きく落ちる方法は、極力避けます。

ブリッジを使用する場合は、そのブリッジのためのコードを書くことになります。後でデータベース固有の ADO.NET データ プロバイダーが使用可能になったとき、このコードを書き直す必要があります。つまり、オブジェクト名、スキーマ情報、エラー処理、およびパラメーターを書き直さなければなりません。ブリッジ用ではなく管理データ プロバイダー用にコード化することによって、貴重な時間とリソースを節約できます。


.NET オブジェクトとメソッドの選択

データの更新