Bulk Insert
作っているアプリケーションで一部バッチ的な処理があるんですが。
OracleにODP.NETでアクセスして、データを何万件か登録するという処理です。
で、普通にOracleCommandでINSERTしてる分には200〜300TPSくらいしかでないです。
まあ、パフォーマンス的にはそんなもんでしょうけど(´ω`)
#環境の詳細は省きますが、特殊な設定をしていない普通のPCです
// 実験用ソース1 using( OracleConnection conn = new OracleConnection( OracleHelper.ConnectionString ) ) using( OracleCommand cmd = new OracleCommand() ) using( OracleParameter parameter1 = new OracleParameter( ":id", OracleDbType.Int32 ) ) using( OracleParameter parameter2 = new OracleParameter( ":data", OracleDbType.NVarchar2, 50 ) ) { conn.Open(); cmd.Connection = conn; cmd.CommandText = "INSERT INTO ptest ( id, data ) VALUES ( :id, :data )"; cmd.CommandType = CommandType.Text; cmd.Parameters.Add( parameter1 ); cmd.Parameters.Add( parameter2 ); for( int count = 0; count < 10000; count++ ) { cmd.Parameters[ 0 ].Value = count; cmd.Parameters[ 1 ].Value = "data" + count.ToString(); int val = cmd.ExecuteNonQuery(); }
で、性能を上げる方法を調べたら、OracleCommandはバッチ配列に対応してるんですね。
OracleCommand.ArrayBindCountに処理件数を設定して、パラメータにも件数分の配列を設定。
これで処理を実行したところ、処理性能は10倍(当社比)にアップしてイイ感じ(・∀・)
// 実験用ソース2 using( OracleConnection conn = new OracleConnection( OracleHelper.ConnectionString ) ) using( OracleCommand cmd = new OracleCommand() ) using( OracleParameter parameter1 = new OracleParameter( ":id", OracleDbType.Int32 ) ) using( OracleParameter parameter2 = new OracleParameter( ":data", OracleDbType.NVarchar2, 50 ) ) { conn.Open(); cmd.Connection = conn; cmd.CommandText = "INSERT INTO ptest ( id, data ) VALUES ( :id, :data )"; cmd.CommandType = CommandType.Text; cmd.Parameters.Add( parameter1 ); cmd.Parameters.Add( parameter2 ); int bindCount = 1000; for( int count = 0; count < 10000; count += bindCount ) { cmd.ArrayBindCount = bindCount; int[] param1 = new int[ bindCount ]; string[] param2 = new string[ bindCount ]; for( int i = 0; i < bindCount; i++ ) { param1[ i ] = count + i; param2[ i ] = "data" + ( count + i ).ToString();; } cmd.Parameters[ 0 ].Value = param1; cmd.Parameters[ 1 ].Value = param2; int val = cmd.ExecuteNonQuery(); } }
あとバッチ配列を使用しない場合のパフォーマンスについて比較しておくと、OracleCommandやOracleParameterを使い回す場合とそうでない場合で20%くらいの性能差がありました。
思ったより差があるもんですが。
ちなみにBulk Insertをやる場合、ADO.NET 2.0でSQL Serverが相手ならSqlBulkCopyを使うって所ですね(´∀`)