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