2005/1/6

不能信任的Oracle Connection Pool

現在大家使用JDBC的時候,應該一定會使用Connection Pooling的機制,這個概念已經變成是常識,所以Connection Pooling的Implementation也非常地多,也許是前人開發的時候,覺得Oracle Database當然要使用Oracle的Connection Pool,所以就用了OracleConnectionPoolDataSource(Oracle公司製作的ojdbc14.jar裡面有個 OracleConnectionPoolDataSource ),可是沒想到困擾了我好一陣子的網站效能問題,竟然發生在這個Pool上面。

我們的平時網站運作還算正常,Server Loading也不重,但總會不定期發生一個問題,網頁上的畫面資料,在向Database下sql command取資料的時候,變得異常地緩慢,一開始覺得是廠商程式寫得太差,因為他把JSP畫面內嵌JDBC程式碼,所以花了一些時間,把所有畫面都改成DAO+Java Bean的模式,另外同時為了改進效能,加入了Cache的概念,利用Singleton與一個簡單的Timer機制,將畫面的資料Cache起來。

這樣做的好處可以避免每一個client連線後,都得向Database取出相同的資料,既可以減少AP Server對Database的loading與latency,也可以加快反應速度,至於後端資料維護的部分,就另外作一個畫面,可以讓資料維護人員點選後將timer重設並重新更新Cache資料。自從運用這個方法後,這些畫面都不再會發生問題了,但還是有一些畫面是根據使用者的個人資料呈現的,所以必須要即時向DB索取資料,問題就發生在這類畫面上。

有些畫面只有一個SQL向DB取資料,但是畫面出現的速度卻明顯地很慢,但這種情況卻不是在Server剛開啟的時候發生,都是跑了半天或一天之後,才會出現這種情形,檢查了DB的連線狀況後才發現一件奇怪的事情,明明是connection pool,為什麼在DB上卻一條連線都沒有看到,而在server reboot之後,就出現了一條連線中的connection,這不是就告訴我,我信任的 Oracle connection pool 竟然出了問題!!

花了不到半小時的時間,把connection pool連線的部分改成proxool,這個套件真不錯,設定方式簡單多元,使用方式也很簡單,也還好我懂得使用一些pattern,沒花多少時間,就可以把整個案子的connection pool換掉,(其實就是整個案子所有的DB connection都是在某一個class裡面的一個static method處理的)。

有了這次教訓,結論就是別太相信你使用的所有外來套件,雖然有基本的信任基礎才會使用它,但是使用過程中還是得保持懷疑的態度,絕對不能把它當作完全正確的東西。另外Open Source/Free Software/Package要有相當數量的使用者背書,那麼這個軟體/套件才具有被信任的基礎,很少人用的東西,自然而然地會有很多bug沒被發現,這個套件也很有可能一下子就消失了,所以擁抱開放原碼就是要擁抱市場,製作開放原碼的專案就是要搶先作市場的領導者。

PS. 也許是我們使用 OracleConnectionPoolDataSource 的設定不夠完整,但反過來說,為什麼 OracleConnectionPoolDataSource 不像 proxool 那麼簡潔有力,設定方式直覺又簡單,使用者發生錯誤的機會也會減少很多,好的軟體/套件一定要以「使用者的需求」為出發點,以愉快的「使用者的使用經驗」為目標。


-----2006/10/23 新增 我得跟Oracle道歉,先前寫的這篇blog並沒有找到真正的原因,發生錯誤的原因是版本問題,而跟connection pool沒有關係,直到今天才發現我應該作這項修正.. -------

症狀:使用jdbc thin driver連接Oracle 8.0.6查詢客戶姓名時,jdbc顯示 "違反協定" 錯誤,db -> AP server的網路流量飆高



發生原因:客戶姓名為特殊字,使用造字檔造字



詳細內容:使用jdbc thin driver連接Oracle 8.0.6查詢客戶姓名時顯示 "違反協定" 錯誤,但db持續透過該連線傳送無效資料(裡面都是00的封包資料)給AP,connection pool視該連線為使用中的狀態,db connection pool無法回收該connection,結果使網路流量飆高,db持續為忙碌狀態效能下降



討論:

1. 此 bug 可以單獨重新複製,故確認問題存在

2. 已測試過oracle網站上所有jdbc版本,都無法正常運作

3. jdbc連接oracle 9i,可正常運作

4. Oracle 8.0.6 短時間內無法升級,因舊有程式(C語言程式)沒有在新版的環境測試過

5. 沒有測試過oracle OCI driver,因OCI driver需要安裝oracle client,不適合AP的環境使用



舒緩劑:

1. 建立一個新的 oracle 9i db,並以dblink連接 oracle 8.0.6,透過jdbc連接9i,然後查詢客戶姓名,即可正常運作

2. 在AP上建立監控機制,監控AP是否已經把connection pool用完,並呈現撈不到資料的狀態,如果是,則發送警告簡訊給系統管理者,重新啟動AP



解藥:還沒找到,目前是無解狀態



註記:

1. 這個問題在發生初期,只有出現網路流量異常的狀況,花了很久的時間debug程式,也找不到問題在哪

2. 後來經由系統使用者回報,某些特殊的客戶,都會產生 "違反協定" 錯誤,經查證後確認問題




-------------------------------

javax.servlet.ServletException: 違反通訊協定



org.apache.jasper.runtime.PageContextImpl.doHandlePageException(PageContextImpl.java:825)

org.apache.jasper.runtime.PageContextImpl.handlePageException(PageContextImpl.java:758)

org.apache.jsp.testdb_jsp._jspService(testdb_jsp.java:137)

org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:94)

javax.servlet.http.HttpServlet.service(HttpServlet.java:810)


org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:324)


org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:292)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:236)