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
公開
Loading