依過去的經驗,Oracle資料庫重啟或連線中斷後,Connection Pool裡會存留一些無效連線,除了IISRESET或重啟程式外,似乎無法透過程式自行剔除Pool裡的連線。但我今天才發現,原來從.NET 2.0起,SqlConnection與OracleConnection早就新增了兩個Static Method可以用來解決類似問題: ClearPoolClearAllPools

寫了以下範例,cnStrMonitor與cnStrTester用了是不同的Username,於是我就可以透過統計v$session特定Username及機器名稱來觀察Oracle觀點看到的連線數。

public static void ShowSessionCount(string tag)
{
    using (OracleConnection cn = new OracleConnection(cnStrMonitor))
    {
        cn.Open();
        OracleCommand cmd = new OracleCommand(@"
select count(*) as c from v$session 
where username = 'schemaName' and machine='machineName'",
            cn);
        OracleDataReader dr = cmd.ExecuteReader();
        dr.Read();
        Console.WriteLine("[{1}] Session Count={0}", dr["c"], tag);
        dr.Close();
        cn.Close();
    }
}
 
public static void TestConnPool()
{
    using (OracleConnection cn = new OracleConnection(cnStrTester))
    {
        ShowSessionCount("Before Open");
        cn.Open();
        ShowSessionCount("After Open");
        OracleCommand cmd = new OracleCommand(
            "select sysdate from dual", cn);
        OracleDataReader dr = cmd.ExecuteReader();
        dr.Read();
        Console.WriteLine(dr[0]);
        dr.Close();
        //OracleConnection.ClearPool(cn);
        ShowSessionCount("Before Close");
        cn.Close();
        ShowSessionCount("After Close");
    }
    ShowSessionCount("After Using");
    OracleConnection.ClearAllPools();
    ShowSessionCount("After ClearAllPools");
}

首先跑第一個測試:

[Before Open] Session Count=0
[After Open] Session Count=1
2009/08/12 上午 04:55:23
[Before Close] Session Count=1
[After Close] Session Count=1
[After Using] Session Count=1
[After ClearAllPools] Session Count=0

如預期地,Connection Pool發生效果,cn.Close()並不會真的將Session關閉,直到using結束(cn.Dispose()),Session一直活著(存在於Pool中),直到ClearAllPools()為止。

接著我們將OracleConnection.ClearPool(cn)那一列的註解拿掉。依ClearPool的文件說明,cn會被先加上註記,在cn.Close()後連線就會被從Pool中清除。

[Before Open] Session Count=0
[After Open] Session Count=1
2009/08/12 上午 05:02:14
[Before Close] Session Count=1
[After Close] Session Count=0
[After Using] Session Count=0
[After ClearAllPools] Session Count=0

由測試結果來看,如同預期,cn.Close()後,Session數就降到零了。

最後這個測試很重要,我們將dr.Close()那一列註解掉,看看會發生什麼事?

[Before Open] Session Count=0
[After Open] Session Count=1
2009/08/12 上午 05:03:56
[Before Close] Session Count=1
[After Close] Session Count=1
[After Using] Session Count=1
[After ClearAllPools] Session Count=1

看到沒? 因為OracleDataReader沒有Close(),即便cn.Close()、cn.Dispose()、ClearAllPools(),連線仍然留存(也許已不在Pool中,但對Oracle來說,Session仍存在),直接Process關閉時才會消失。

這個實驗突顯了一個事實,看來Connection.Close()並非完整釋放連線的保證。因此切記要養成DataReader用完就Close()的習慣,不能光依賴Connection.Close()。如果不放心,用using包住DataReader應是不錯的做法。

若不想讓多餘的Oracle連線耗用不必要的資源,記得要OracleDataReader.Close()哦!

PS:我有點好奇這是By Design或是Bug,但在Windows 2003 + VS 2005 + Oracle Client 9.2、Windows 2008 + VS2008 + Oracle Client 10.2連線Oracle 9.2 DB都得到同樣結果,不像單一Oracle Client版本的問題,我想養成關閉DataReader的習慣有益無害。大家如果有其他版本的Oracle環境也幫忙測看看吧!


Comments

# by 好奇寶寶

不曉得sqlDataReader 是否也是同樣狀況呢?

# by Billy

這是System.Data.OracleClient 和 ODP.net 都會出現的問題嗎﹖

# by johnson

如果 con.close(); con.dispose(); 是否就會把session降低

# by Jeffrey

to johnson, 剛好最近看到一篇探討OracleConnection Dispose()對Pooling影響的文章: http://bit.ly/daEicy ,不知有沒有解答你的疑惑?

# by DT

黑暗大~ 小弟實驗發現只要OracleConnection.ClearAllPools()就行了 感覺OracleConnection.ClearPool(cn)其實沒有效 XD 再次感謝黑暗大的分享... 因為這個blog幫我解決了好多工作上的問題^^

# by ALEX

請問黑大, 用這個Method清除連線後,原本Pool的優勢就沒有了,是嗎?

# by Jeffrey

to ALEX,會呼叫ClearPool、ClearAllPools多是因為Pool中有故障或異常的連線,而這兩個方法是一次性的動作,清除後Pool會繼續發揮作用。

Post a comment