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)

7 則留言:

  1. 您好
    關於proxool的使用
    想請教一下
    QuickStart裡面的
    connection = DriverManager.getConnection("proxool.example:org.hsqldb.jdbcDriver:jdbc:hsqldb:test");

    對應到我原先的JDBC用法
    jdbc:oracle:thin:user/passwd@127.0.0.1:1521:server1

    應該怎麼改寫呢 ?
    proxool.example 相當於 jdbc:oracle:thin?
    還是 ...

    麻煩分享一下囉 :)

    回覆刪除
  2. 關於這個問題 你可以參考 http://www.javaworld.com.tw/confluence/pages/viewpage.action?pageId=703

    但通常使用proxool都是先在 proxool.properties 或 proxool.xml 中設定好相關資料
    然後以 alias 連接DB
    取connection就是用
    connection = DriverManager.getConnection("proxool.aliasname");

    回覆刪除
  3. 謝謝!
    我可以正常使用了

    另外想請教 我看到一些文件討論
    不建議使用 DriverManager
    proxool 是不是可以適用 DataSource呢?

    回覆刪除
  4. 請先參考 http://www.javaworld.com.tw/jute/post/view?bid=21&id=129676&sty=1&tpg=1&age=0

    不管是用DataSource或是DriverManager,重點是就是要使用DB Connection Pool

    只要你的程式裡面有將所有取資料庫連線的部分寫成一個單獨的API回傳一個可以使用的Connection,不管是用DS或是DriverManager,其實都是很容易修改的。

    我用過 Oracle Driver 提供的oracle.jdbc.pool.OracleDataSource,也嘗試在jboss裡做ds設定,然後透過JNDI去取得DataSource,也用過Proxool,不管什麼方式都是可以work的。

    回覆刪除
  5. 看起來的症狀可能是傳遞資料時沒有用base64編碼,使的某些字造成錯誤。
    解決方法->要看source追問題了

    ps.以上僅提供建議,我並不精通java

    回覆刪除
  6. 不好意思,我看了你的說明很久.但對於proxool的用法仍然無法成功.

    我的做法是:
    1.先將proxool下載解壓的lib去
    2.修改java檔案
    3.在complie時 會出現org.logicalcobwebs.proxool.configuration
    error

    我確認過 classpath.但仍無法使用
    是否可以請教您 那裡出問題了呢?

    回覆刪除
  7. complie時 會出現問題 應該是 classpath 問題,你給的資訊不夠多,不知道問題到底在哪

    試試看去查閱一下 proxool 的文件 http://proxool.sourceforge.net/quickStart.html

    也可以去 javaworld@tw 貼一下文章問問看

    回覆刪除