ORACLE Transaction大車拼!

為了測試使用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實作一大截,在講究效能的場合,要特別留意。

歡迎推文分享:
Published 28 December 2007 08:04 PM 由 Jeffrey
Filed under: , , ,
Views: 16,765



意見

# Ark said on 16 May, 2010 01:10 PM

System.Data.OracleClient 9208

在分散式(IsolationLevel.ReadCommitted)與非分散式交叉使用時會遇到

不定時的ORA-01453

暫推斷為conntion pool機制 和isolation level 卡卡..造成

removing isolation level?clear pool?

你的看法呢?

(必要的) 
(必要的) 
(選擇性的)
(必要的) 
(提醒: 因快取機制,您的留言幾分鐘後才會顯示在網站,請耐心稍候)

5 + 3 =

搜尋

Go

<December 2007>
SunMonTueWedThuFriSat
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345
 
RSS
創用 CC 授權條款
【廣告】
twMVC

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


Syndication