ODP.NET パフォーマンスチューニング:FetchSizeプロパティ
アプリケーションのパフォーマンスは、アプリケーションがフェッチする必要のある行数と、行の検索に必要なデータベース・ラウンドトリップの回数に依存します。
Oracle Data Provider for .NET開発者ガイド11g リリース1(11.1) より引用
との事です。
具体的には、1回のデータベース・ラウンドトリップで取り出したデータをキャッシュするためのメモリサイズをFetchSizeプロパティにセットして、複数の行をキャッシュ出来るようにしデータベース・ラウンドトリップを少なくするという事になります。
ここでは、サンプルを作り実験してみようと思います。
FetchSizeプロパティ
FetchSizeプロパティは、OracleCommandまたはOracleDataReaderのどちらにも設定できます。OracleCommandのFetchSizeプロパティを設定した場合、新しく作成されるOracleDataReaderにはOracleCommandのFetchSizeプロパティが継承されます。継承されたFetchSizeはそのままにすることも、変更してオーバーライドすることもできます。
設計時にフェッチサイズがわかっている場合にはあらかじめプロパティを設定してもいいですが、わからない場合(の方が多いと思う)、OracleCommandのRowSizeプロパティで1行あたりのバイト数を取得し、適当と考えられる行数を乗じてFetchSizeプロパティを設定します。
あまりFetchSizeが大きいとクライアントのメモリを圧迫しますし、小さいとデータベース・ラウンドトリップが増えるので微調整が必要となります。
サンプル
EMP表と構造が同じEMPTEST表を1行ずつ読んでSAL列の合計を計算してみます。(EMPTESTには100万件のレコードがあります)
Const ConnectionString As String = "DATA SOURCE=ORCL;User ID=scott;password=tiger;" Const cmdSelect As String = "select SAL from EMPTEST" '''''' FetchSizeプロパティを指定しない(デフォルト65536) ''' ''' ''' Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim Total_Sal As Decimal = 0D Dim rd As OracleDataReader = Nothing Dim sw As New System.Diagnostics.Stopwatch() Dim dCount As Integer = 0 Try Using cn As New OracleConnection(ConnectionString) cn.Open() Using cmd As New OracleCommand(cmdSelect, cn) 'ストップウォッチを開始する sw.Start() rd = cmd.ExecuteReader While rd.Read Total_Sal += rd.GetDecimal(0) End While rd.Close() 'ストップウォッチを止める sw.Stop() '結果を表示する TextTotal_SAL1.Text = Total_Sal.ToString TextTime1.Text = sw.Elapsed.ToString End Using cn.Close() End Using Catch ex As Exception MessageBox.Show(ex.Message) Finally If Not IsNothing(rd) Then If Not rd.IsClosed Then rd.Close() End If rd.Dispose() End If End Try End Sub ''' ''' FetchSizeプロパティを指定 ''' ''' ''' '''Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click Dim Total_Sal As Decimal = 0D Dim rd As OracleDataReader = Nothing Dim sw As New System.Diagnostics.Stopwatch() Dim dCount As Integer = 0 Try Using cn As New OracleConnection(ConnectionString) cn.Open() Using cmd As New OracleCommand(cmdSelect, cn) 'ストップウォッチを開始する sw.Start() rd = cmd.ExecuteReader rd.FetchSize = cmd.RowSize * CInt(TextRow.Text) TextFetchSize.Text = rd.FetchSize.ToString While rd.Read Total_Sal += rd.GetDecimal(0) End While rd.Close() 'ストップウォッチを止める sw.Stop() '結果を表示する TextTotal_SAL2.Text = Total_Sal.ToString TextTime2.Text = sw.Elapsed.ToString End Using cn.Close() End Using Catch ex As Exception MessageBox.Show(ex.Message) Finally If Not IsNothing(rd) Then If Not rd.IsClosed Then rd.Close() End If rd.Dispose() End If End Try End Sub
実験結果
結果から言うと、自分の環境では微妙なパフォーマンスの差しか見られませんでした。以下の実行結果を記載します。
Fetch Size (Byte) | FetchSize 無指定時 実行時間(秒) |
FetchSize 指定時 実行時間(秒) |
|
---|---|---|---|
FetchSize 65504 Byte (行数:2047) (デフォルトの65536Byteと近い値) |
1回目 |
2.0746 |
2.0475 |
2回目 |
2.2848 |
2.0715 |
|
FetchSize 160,000Byte (行数: 5000) |
1回目 |
2.1758 |
2.1059 |
2回目 |
2.4743 |
2.0846 |
|
FetchSize 320,000Byte (行数: 10000) |
1回目 |
2.1276 |
2.0250 |
2回目 |
2.0720 |
2.0229 |
|
FetchSize 3,200,000Byte (行数: 100000) |
1回目 |
2.0574 |
2.0430 |
2回目 |
2.0909 |
2.0259 |
どれもFetchSizeを明示的に指定した方が速度がわずかに上回っています。
FetchSizeの件数別にいうと、このケースだと「320,000Byte10,000行分」が最もパフォーマンスがよかったです。しかしながらPC自体のパフォーマンスを考慮すると、残念ながら今回のケースではあまり変わりませんでした。
しかし、ネットワークに流れる回数が減る事によって、ネットワークのパフォーマンスは向上すると考えられるので、積極的に利用するのがいいと思います。
実行画面
履歴
- 2011/07/17
- 公開