ORACLE Transaction大車拼!
| | | 1 | |
為了測試使用System.Data.OracleClient、ODP.NET(Oracle.DataAccess.Client)與TransactionScope三者的效能差距,我設計了以下的實驗,分別用三種方式(因ODP.NET 9207還不支援TransactionScope,所以TransactionScope搭配System.Data.OracleClient使用)將三個Insert動作包成Transaction,並各測十次。
static object obj = new object();
static int _idx;
static int getIdx()
{ lock (obj) {_idx++;
return _idx;}
}
static void testOraTrn1()
{using (OracleConnection cn = new OracleConnection(cnStr))
{cn.Open();
OracleTransaction trn = cn.BeginTransaction();
OracleCommand cmd = new OracleCommand();cmd.Connection = cn;
cmd.Transaction = trn;
cmd.CommandText = "INSERT INTO Jeffrey Values (:EmpNo, :EmpName)";OracleParameter pEmpNo =
cmd.Parameters.Add("EmpNo", OracleType.Int32);OracleParameter pEmpName =
cmd.Parameters.Add("EmpName", OracleType.VarChar); try {pEmpNo.Value = getIdx();
pEmpName.Value = "T1-1st";cmd.ExecuteNonQuery();
pEmpNo.Value = getIdx();
pEmpName.Value = "T1-2nd";cmd.ExecuteNonQuery();
pEmpNo.Value = getIdx();
pEmpName.Value = "T1-3rd";cmd.ExecuteNonQuery();
trn.Commit();
}
catch {trn.Rollback();
}
}
}
static void testOraTrn2()
{ using (ODP.OracleConnection cn = new Oracle.DataAccess.Client.OracleConnection(cnStr)) {cn.Open();
ODP.OracleTransaction trn = cn.BeginTransaction();
ODP.OracleCommand cmd = cn.CreateCommand();
cmd.Connection = cn;
cmd.CommandText = "INSERT INTO Jeffrey Values (:EmpNo, :EmpName)";ODP.OracleParameter pEmpNo =
cmd.Parameters.Add("EmpNo", ODP.OracleDbType.Int32);ODP.OracleParameter pEmpName =
cmd.Parameters.Add("EmpName", ODP.OracleDbType.Varchar2); try {pEmpNo.Value = getIdx();
pEmpName.Value = "T2-1st";cmd.ExecuteNonQuery();
pEmpNo.Value = getIdx();
pEmpName.Value = "T2-2nd";cmd.ExecuteNonQuery();
pEmpNo.Value = getIdx();
pEmpName.Value = "T2-3rd";cmd.ExecuteNonQuery();
trn.Commit();
}
catch {trn.Rollback();
}
}
}
static void testOraTrn3()
{using (TransactionScope tx = new TransactionScope())
{using (OracleConnection cn = new OracleConnection(cnStr))
{cn.Open();
OracleCommand cmd = new OracleCommand();cmd.Connection = cn;
cmd.CommandText = "INSERT INTO Jeffrey Values (:EmpNo, :EmpName)";OracleParameter pEmpNo =
cmd.Parameters.Add("EmpNo", OracleType.Int32);OracleParameter pEmpName =
cmd.Parameters.Add("EmpName", OracleType.VarChar); try {pEmpNo.Value = getIdx();
pEmpName.Value = "T3-1st";cmd.ExecuteNonQuery();
pEmpNo.Value = getIdx();
pEmpName.Value = "T3-2nd";cmd.ExecuteNonQuery();
pEmpNo.Value = getIdx();
pEmpName.Value = "T3-3rd";cmd.ExecuteNonQuery();
tx.Complete();
}
catch { //Log error or something}
}
}
}
static void trnTest(int testNo)
{ int times = 5; long total = 0;for (int i = 0; i < times; i++)
{ Stopwatch sw = new Stopwatch();sw.Start();
switch (testNo) { case 1: testOraTrn1(); break; case 2: testOraTrn2(); break; case 3: testOraTrn3(); break;}
sw.Stop();
Console.WriteLine("TEST{0}-{1:00} {2}ms",testNo, i, sw.ElapsedMilliseconds);
total += sw.ElapsedMilliseconds;
}
Console.WriteLine("TEST{0} Avg = {1}ms",testNo, total / times);
}
測試結果出籠:
TEST1-00 350ms
TEST1-01 5ms
TEST1-02 4ms
TEST1-03 5ms
TEST1-04 4ms
TEST1 Avg = 73ms
TEST2-00 281ms
TEST2-01 7ms
TEST2-02 4ms
TEST2-03 4ms
TEST2-04 5ms
TEST2 Avg = 60ms
TEST3-00 1054ms
TEST3-01 8ms
TEST3-02 9ms
TEST3-03 8ms
TEST3-04 8ms
TEST3 Avg = 217ms
賽前預測,TransactionScope會是最慢的,實測結果也是如此。
不過由數字來看,三者都慢在第一次,推論應是第一次建立Connection較花時間,但之後Connection Pooling發威,速度就變快許多。
我的結論是,TransactionScope雖然方便且具備一些不可取代的涵蓋性(例如: 異質資料庫間的交易、包入3rdParty元件內的資料庫更新等),但分散式交易在效能上輸給單一資料庫的Transaction實作一大截,在講究效能的場合,要特別留意。
Comments
# by Ark
System.Data.OracleClient 9208 在分散式(IsolationLevel.ReadCommitted)與非分散式交叉使用時會遇到 不定時的ORA-01453 暫推斷為conntion pool機制 和isolation level 卡卡..造成 removing isolation level?clear pool?