OracleConnection Pooling及OracleDataReader.Close()
7 | 22,057 |
依過去的經驗,Oracle資料庫重啟或連線中斷後,Connection Pool裡會存留一些無效連線,除了IISRESET或重啟程式外,似乎無法透過程式自行剔除Pool裡的連線。但我今天才發現,原來從.NET 2.0起,SqlConnection與OracleConnection早就新增了兩個Static Method可以用來解決類似問題: ClearPool、ClearAllPools。
寫了以下範例,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會繼續發揮作用。