ADO.NET Oracle用データプロバイダの性能傾向の違い
同日の訂正記事でも書いたが、元々処理の経過時間を計測するコードを用意したのはOracleに接続可能な複数のADO.NETデータプロバイダの性能の傾向を調べるのが目的だった。
.NET2.0環境においてADO.NETを用いてOracleデータベースに接続するためには以下のデータプロバイダの何れかを使用することが多いだろう。
- Oracle Data Provider for .NET (Oracle Data Access Software for Windows)
- .NET Managed Provider for Oracle 日本語版
- OLE DB .NET Data Provider
このうち、Oracle Data Provider for .NET (以降はODP.NETと略する)はOracle謹製であり、.NET Managed Provider for OracleはMicrosoftが提供しているアドオンのマネジドプロバイダだ。最後のOLEDBプロバイダは元々ADO.NETに添付されている汎用のOLEDBプロバイダをそのまま使う方法である。
それぞれは全く違うデータプロバイダに見えるが、以前の日記にも書いたがその要件には必ず
とあり、内部の実装では最終的にOCIを呼びNETxプロトコルを使ってサーバと通信するネィティブブリッジ型であることが推測される。
内部的にOCIを呼んでいるのであれば、これら各プロバイダ毎に性能の違いは本当にあるのかということが気になったため、実際に限定した条件の中で性能を比べてみることにした。
計測に使用するデータはテスト用のシンプルなテーブルを用意して、ランダムなデータを3000件挿入した。それぞれのカラムにはある程度の文字列を挿入してあり、NULL値になっているカラムは一つも無い。
CREATE TABLE PROVTEST ( COLUMN1 VARCHAR2 255, COLUMN3 VARCHAR2 255, COLUMN4 VARCHAR2 255, COLUMN5 VARCHAR2 255, UPDATE VARCHAR2 14 );
計測する時間はデータベースの接続に要した時間(DbConnection#Openが完了するまでの時間)と全ての行と列の読み取りにかかる時間とし、以下のようなコードを用いる。
#if ODPNET DbConnection con = new Oracle.DataAccess.Client.OracleConnection(); con.ConnectionString = "User Id=KAZZZ;Password=KAZZZ;Data Source=ORCL;"; #elif MSORACLE DbConnection con = new System.Data.OracleClient.OracleConnection(); con.ConnectionString = "User Id=KAZZZ;Password=KAZZZ;Server=ORCL;"; #elif OLEDB DbConnection con = new System.Data.OleDb.OleDbConnection(); con.ConnectionString = "Provider=MSDAORA.1;User ID=KAZZZ;password=KAZZZ;Data Source=ORCL;Persist Security Info=False"; #endif try { Stopwatch sw = new Stopwatch(); sw.Start(); con.Open(); sw.Stop(); Debug.Write("接続に要した時間 = " + sw.Elapsed.TotalMilliseconds + "ミリ秒 、"); DbCommand cmd = con.CreateCommand(); cmd.CommandText = "SELECT * FROM PROVTEST"; sw.Reset(); sw.Start(); DbDataReader reader = cmd.ExecuteReader(); while (reader.Read()) { for(int i = 0; i < reader.FieldCount; i++) { Console.Write(reader[i].ToString() + ", "); } Console.WriteLine(""); } } finally { sw.Stop(); Debug.WriteLine("読込みに要した時間 = " + sw.Elapsed.TotalMilliseconds + "ミリ秒"); cmd.Dispose(); con.Close(); con.Dispose(); }
この様なコードを連続して3回動かして、最も短かった時間をサンプル値とする。更にこれを3回繰り返して平均をサンプル値とする。
各データプロバイダの切り替えだが、.NET2.0ではADO.NETにプロバイダには依存しない基本クラスが用意されたのでビルドパラメタで全ての元となるDbConnectionクラスのインスタンスを生成する際に、プロバイダ独自のクラスのコンストラクタを呼ぶようにしている。
例えばODP.NETであればビルドパラメタか"#define ODPNET"を指定することで、Oracle.DataAccess.Client.OracleConnectionクラスのインスタンスを生成する。そこから生成されるDbCommandクラスのインスタンスとDbDataReaderクラスのインスタンスはそれぞれ、Oracle.DataAccess.Client.OracleCommandクラス、Oracle.DataAccess.Client.OracleDataReaderクラスのインスタンスとなる訳だ。
ではこのテストをそれぞれのプロバイダで実行してみよう。(OracleサーバとはLAN経由TNS NETプロトコルで接続しており、プラットホームはLinux、Oracle 9iがインストールされている環境だ)
接続に要した時間 = 244.1747ミリ秒、 3000件のレコード読み込みに要した時間 1715.2763ミリ秒 接続に要した時間 = 8.8628ミリ秒、 3000件のレコード読み込みに要した時間 1642.5261ミリ秒 接続に要した時間 = 0.1758ミリ秒、 3000件のレコード読み込みに要した時間 1587.5216ミリ秒
接続に要した時間 = 366.6578ミリ秒、 3000件のレコード読み込みに要した時間 2505.9952ミリ秒 接続に要した時間 = 1.8901ミリ秒、 3000件のレコード読み込みに要した時間 2299.7433ミリ秒 接続に要した時間 = 0.1761ミリ秒、 3000件のレコード読み込みに要した時間 2496.9919ミリ秒
- OLE DB .NET Data Provider
接続に要した時間 = 338.6625ミリ秒、 3000件のレコード読み込みに要した時間 1786.5696ミリ秒 接続に要した時間 = 1.1791ミリ秒、 3000件のレコード読み込みに要した時間 1651.4263ミリ秒 接続に要した時間 = 0.6761ミリ秒、 3000件のレコード読み込みに要した時間 1691.5705ミリ秒
という結果となった。二度目以降の接続にかかる時間が極端に短くなっているのは接続プールのおかげと見て良いのだろうか。しかし腑に落ちないのは接続プールが有効にならないはずのOLE DB .NET Data Providerによる接続でも二度目以降の接続時間は、他のプロバイダ同様の傾向を見せていることだ。従ってこれは接続プールの効果では無い可能性もある(ソケット接続の再利用が促進されただけ等)
当初はデータベースベンダー提供のプロバイダとMS謹製のプロバイダ、最も汎用の性能的には不利なはずのOLEDBプロバイダを鑑みて、接続、読み込みの性能共に
という順位を予測したのだが、実際には
という順位となった。ODP.NETが優位なのは当たり前だが、OLEDBが意外に速い。より高い性能を搾り出す必要がある場合にはODP.NETを使うのがベストであることは疑いの余地は無いがOLEDBもかなり健闘している。それにしても、.NET Managed Provider for Oracleは他の二つに比べても明らかにワンランク下の性能だ。ダウンロードページによると更新が2002/07/31とあるが、これが本当だとすると.NET 2.0に最適化されていないのが原因である可能性もあるだろう。引き続き検証が必要だが、全てにおいて同じ傾向であれば.NET Managed Provider for OracleはダウンロードしてわざわざGACにインストールする意味は無いかもしれない。
※※なお、このテスト結果は私個人が参考するための個人的且つ限定的な使用環境における性能の傾向であり、ADO.NETプロバイダにおける厳密な性能比較を表したベンチマークではないことをお断りしておく※※