ADO.NETパフォーマンスの向上


データアクセスのパフォーマンスに悪影響を及ぼし得る主な問題

非効率なインデックスまたはインデックスなし - インデックスがなければ、全テーブルのスキャンが必要となるため、クエリは非効率になります。また、データが増えるにつれ、テーブルが断片化していくかもしれません。さらに、インデックスを定期的に再構築しないと、クエリ パフォーマンスは低下し得ます。

開いている接続が多すぎる - 接続は、高くつく希少なリソースで、接続プーリングによって呼び出し元の間で共有すべきです。呼び出し元ごとに接続を開くと、スケーラビリティは制限されます。接続プーリングを効率的に利用するには、接続を開いたままにしないようにし、接続文字列の変更を避けてください。


接続

DataReader 使用時は CommandBehavior.CloseConnection を指定する
DataReader オブジェクトを作成時に、ExecuteReader の呼び出しにおいて CommandBehavior.CloseConnection 列挙型を指定してください。これにより、DataReader を閉じると、接続も閉じられるようになります。
' 接続とコマンドを作成し、接続を開く
Dim myReader AS SqlDataReader
. . .
myReader= myCommand.ExecuteReader(CommandBehavior.CloseConnection)
' データを読み取る
. . .
myReader.Close() '接続とリーダーが閉じられる

1 つの処理について Fill または Update を使う場合は接続を明示的に開かない
1 つの Fill 処理または Update 処理を実行する場合、Fill メソッドを呼び出す前に接続を開かないでください。DataAdapter が自動的に接続を開閉するためです。
Dim dSet As DataSet = new DataSet("test")
Dim cn As SqlConnection = new SqlConnection(connString)
Dim cmd As SqlCommand = new SqlCommand(sqlQuery,cn)
Dim dAdapter As SqlDataAdapter = new SqlDataAdapter(cmd)
dAdapter.Fill(dSet) ' 接続は明示的に開かれていない
' DataAdapter が自動的に接続を開閉する


コマンド

データを返さないコマンドには ExecuteNonQuery を使う
データを受けないコマンドを実行する場合は、ExecuteNonQuery メソッドを使ってください。例えば、以下のタイプのコマンドには、ExecuteNonQuery を使います。
  • CREATE、ALTER などのデータ定義言語コマンド
  • INSERT、UPDATE、DELETE などのデータ操作言語コマンド
  • GRANT、REVOKE などのデータ制御言語コマンド
Dim cn As SqlConnection = new SqlConnection(connString)
Dim cmd As SqlCommand = new SqlCommand
("UPDATE Customer SET Freight = 45.44 WHERE CustomerID = 10248", cn)
cmd.ExecuteNonQuery()

1つの値を返すには ExecuteScalar を使う
COUNT(*) や SUM(Price) などの関数を使ってクエリから 1 つの値を取得するなら、ストアド プロシージャ出力パラメータを使い、それから Command.ExecuteNonQuery メソッドを用いることが可能です。これにより、結果セットを作成することに伴うオーバーヘッドをなくすことができます。
以下のストアド プロシージャは、Customers テーブルの行数を返します。
CREATE PROCEDURE GetNumberOfCustomers(@CustomerCount INT OUTPUT)
AS
SELECT @CustomerCount = COUNT(*)
FROM Customers

非常に幅の広い行や BLOB(画像や音声などのバイナリデータを格納できるデータ型) を含む行には CommandBehavior.SequentialAccess を使う
非常に幅の広い行やバイナリ ラージ オブジェクト (BLOB) を含む行には、CommandBehavior.SequentialAccess 列挙型を使用してください。これにより、行全体を返す代わりに、取得した行の指定バイトのみを返せるようになります。BLOB データを含む行をすべて返すと、大量のメモリを消費することになりかねません。

CommandBehavior.SequentialAccess を使えば、BLOB データは参照時にのみ取得することになります。例えば、GetBytes メソッドを呼び出すことができます。GetBytes メソッドにより、読み取りバイト数を厳密にコントロール可能となります。以下のコードで、CommandBehavior.SequentialAccess の使い方を示します。
Dim reader As SqlDataReader = 
cmd.ExecuteReader(CommandBehavior.SequentialAccess)
また、非常に幅の広い行や BLOB データを含む行を伴うテーブルに対し、楽観的ロックを実行する場合は、タイムスタンプを使ってください。テーブルに内の全フィールドをオリジナルのデータと比べる代わりにタイムスタンプを使えば、パラメータを n / 2 + 1 だけ減らすことができます。

実行時に CommandBuilder を使わない
SqlCommandBuilder や OleDbCommandBuilde などの CommandBuilder オブジェクトは、DataAdapter の InsertCommand、UpdateCommand、DeleteCommand の各プロパティを自動生成します。CommandBuilder オブジェクトは、これらのプロパティを DataAdapter の SelectCommand プロパティに基づいて生成します。アプリケーションの設計時やプロトタイプ作成時には、この CommandBuilder オブジェクトを役立てることができます。しかし、このオブジェクトを製品版アプリケーションに使用するべきではありません。コマンド生成に必要な処理は、パフォーマンスに影響を及ぼします。コマンドのためにストアド プロシージャを手動で作成するか、Visual Studio® .NET の設計ウィザードを利用し、必要に応じてカスタマイズしてください。


DataSet vs. DataReader

複数行のデータを取得し、何らかの表示や処理をする場合のアプローチとして、基本的な選択肢が 2 つあります。DataSet オブジェクトを使用するか、DataReader オブジェクトを使用するかです。

DataReader アプローチは通常、DataSet オブジェクトの作成に伴うオーバーヘッドを伴わないため、より高速です。DataSet オブジェクトに関連するオーバーヘッドとしては、DataTable、DataRow、DataColumn などの DataSet サブオブジェクトの作成が挙げられます。反面、DataReader は柔軟性に欠け、複数階層を持つアプリケーションでデータをキャシュし、コンポーネントへ送るようなシナリオには不適切です。

メモ: DataSet を満たすために使われる DataAdapter は、内部で DataReader を使います。

以下が当てはまる状況では、DataReader を使ってください。
  • 前方参照のみ、かつ読み取り専用のデータ アクセスが必要で (一方向シナリオ)、データにできるだけ速くアクセスでき、なおかつキャッシュする必要がない。
  • ビジネス コンポーネントのような、データを保持できるデータ コンテナがある。

以下が当てはまる状況では、DataSet を使ってください。
  • レイヤ間でデータのキャッシュや受け渡しを行わなければならない。
  • XML 操作または非 XML 操作のために、データのインメモリ リレーショナル ビューを必要とする。
  • 取得行の一部または全部を更新する必要があり、SqlDataAdapter クラスのバッチ更新機能を利用したい。
  • データを、DataReader に連結できないコントロール型に連結する必要がある。データ連結可能な Windows フォーム コントロール の多くは、IList インターフェイスを実装するデータ ソースを必要とします。DataSet はIList を実装しますが、DataReader は Ienumerable を実装します。IEnumerable は、ほとんどの Web フォーム コントロールへの連結をサポートしますが、一定の Windows フォーム コントロールへの連結はサポートしません。連結したいコントロール型について、データ ソース要件を確認してください。
  • 複数のデータ群に同時にアクセスする必要があるが、サーバー リソースをオープンなままにしたくない。


ネット「マイクロソフト 第12章「ADO.NET パフォーマンスの向上」

DataSet vs. DataReader 速度比較


Records SQL Client DataReader SQL Client DataTable OLEDB DataReader OLEDB DataTable
1 0.0000000 0.0801152 0.0000000 0.1001440
10 0.0000000 0.1001440 0.200288 0.9012960
20 0.0000000 0.1802592 0.100144 1.9027360
30 0.0100144 0.2804032 0.100144 2.8040320
40 0.0100144 0.3705328 0.100144 3.7053280
50 0.0100144 0.4706768 0.200288 4.7067680
60 0.0100144 0.5608064 0.200288 5.6080640
70 0.0200288 0.6509360 0.200288 6.5093600
80 0.0200288 0.7410656 0.200288 7.4106560
90 0.0200288 0.8412096 0.300432 8.3119520
100 0.0300432 0.9313392 0.300432 9.3133920
200 0.0600864 1.7825630 0.600864 17.8256300
300 0.0801152 2.6638310 0.901296 26.7384500
400 0.1101584 3.5551120 1.101584 35.5511200
500 0.1402016 4.4463930 1.402016 44.4639400
600 0.1702448 5.3376750 1.802592 53.3767500
700 0.2002880 6.2289570 2.00288 62.6901400
800 0.2303312 7.1202380 2.303312 71.1022400
900 0.2703888 8.0015060 2.603744 80.0150500
1000 0.2904176 8.8927870 2.904176 88.9278700
2000 0.6108784 18.3463800 6.00864 178.1562000
3000 0.9313392 26.7785100 9.113104 267.3845000
4000 1.2317710 35.7013400 12.21757 356.5126000
5000 1.5221890 44.6542100 15.42218 446.0414000
6000 1.8827070 53.8073700 18.4265 553.4959000
7000 2.1931540 63.5113300 21.03024 636.4151000
8000 2.4334990 72.8046900 23.93442 726.8452000
9000 2.7639740 81.7876100 27.43946 832.7975000
10000 3.0844350 91.6217400 31.14478 911.7110000


ネット「ADO.NETでのデータ取得を高速化するためのヒント」

トータル: - /今日: - /昨日: -
最終更新:2009年09月04日 12:41