<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Rising Sun

      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
      148 隨筆 :: 0 文章 :: 22 評論 :: 0 Trackbacks

    Spring的事務管理難點剖析(7):數據連接泄漏
    底層連接資源的訪問問題 

       對于應用開發者來說,數據連接泄漏無疑是一個可怕的夢魘。只要你開發的應用存在數據連接泄漏的問題,應用程序最終都將因數據連接資源的耗盡而崩潰,甚至還可能引起數據庫的崩潰。數據連接泄漏像一個黑洞那樣讓開發者避之唯恐不及。 
       Spring DAO對所有支持的數據訪問技術框架都使用模板化技術進行了薄層的封裝。只要你的程序都使用Spring DAO的模板(如JdbcTemplate、HibernateTemplate等)進行數據訪問,一定不會存在數據連接泄漏的問題——這是Spring給予我們的鄭重承諾!如果使用Spring DAO模板進行數據操作,我們無須關注數據連接(Connection)及其衍生品(Hibernate的Session等)的獲取和釋放操作,模板類已經通過其內部流程替我們完成了,且對開發者是透明的。 
       但是由于集成第三方產品、整合遺產代碼等原因,可能需要直接訪問數據源或直接獲取數據連接及其衍生品。這時,如果使用不當,就可能在無意中創造出一個魔鬼般的連接泄漏問題。 
    我們知道:當Spring事務方法運行時,就產生一個事務上下文,該上下文在本事務執行線程中針對同一個數據源綁定了一個唯一的數據連接(或其衍生品),所有被該事務上下文傳播的方法都共享這個數據連接。這個數據連接從數據源獲取及返回給數據源都在Spring掌控之中,不會發生問題。如果在需要數據連接時,能夠獲取這個被Spring管控的數據連接,則使用者可以放心使用,無須關注連接釋放的問題。 
        那么,如何獲取這些被Spring管控的數據連接呢?Spring提供了兩種方法:其一是使用數據資源獲取工具類;其二是對數據源(或其衍生品如Hibernate SessionFactory)進行代理。 

    Spring JDBC數據連接泄漏 

       如果我們從數據源直接獲取連接,且在使用完成后不主動歸還給數據源(調用Connection#close()),則將造成數據連接泄漏的問題。 
    1. package com.baobaotao.connleak;  
    2. …  
    3. @Service("jdbcUserService")  
    4. public class JdbcUserService {  
    5.     @Autowired  
    6.     private JdbcTemplate jdbcTemplate;  
    7.   
    8.     @Transactional  
    9.     public void logon(String userName) {  
    10.         try {  
    11.   
    12.             //①直接從數據源獲取連接,后續程序沒有顯式釋放該連接  
    13.             Connection conn = jdbcTemplate.getDataSource().getConnection();  
    14.             String sql = "UPDATE t_user SET last_logon_time=? WHERE user_name =?";  
    15.             jdbcTemplate.update(sql, System.currentTimeMillis(), userName);  
    16.   
    17.             //②模擬程序代碼的執行時間  
    18.             Thread.sleep(1000);   
    19.         } catch (Exception e) {  
    20.             e.printStackTrace();  
    21.         }  
    22.     }  
    23. }  

       JdbcUserService通過Spring AOP事務增強的配置,讓所有public方法都工作在事務環境中,即讓logon()和updateLastLogonTime()方法擁有事務功能。在logon()方法內部,我們在①處通過調用jdbcTemplate.getDataSource().getConnection()顯式獲取一個連接,這個連接不是logon()方法事務上下文線程綁定的連接,所以如果開發者沒有手工釋放這個連接(顯式調用Connection#close()方法),則這個連接將永久被占用(處于active狀態),造成連接泄漏!下面,我們編寫模擬運行的代碼,查看方法執行對數據連接的實際占用情況: 
    Java代碼  收藏代碼
    1. package com.baobaotao.connleak;  
    2. …  
    3. @Service("jdbcUserService")  
    4. public class JdbcUserService {  
    5.     …  
    6.     //①以異步線程的方式執行JdbcUserService#logon()方法,以模擬多線程的環境  
    7.     public static void asynchrLogon(JdbcUserService userService, String userName) {  
    8.         UserServiceRunner runner = new UserServiceRunner(userService, userName);  
    9.         runner.start();  
    10.     }  
    11.     private static class UserServiceRunner extends Thread {  
    12.         private JdbcUserService userService;  
    13.         private String userName;  
    14.         public UserServiceRunner(JdbcUserService userService, String userName) {  
    15.             this.userService = userService;  
    16.             this.userName = userName;  
    17.         }  
    18.         public void run() {  
    19.             userService.logon(userName);  
    20.         }  
    21.     }  
    22.       
    23.    //②讓主執行線程睡眠一段指定的時間  
    24.    public static void sleep(long time) {  
    25.         try {  
    26.             Thread.sleep(time);  
    27.         } catch (InterruptedException e) {  
    28.             e.printStackTrace();  
    29.         }  
    30.     }  
    31.   
    32.    //③匯報數據源的連接占用情況  
    33.     public static void reportConn(BasicDataSource basicDataSource) {  
    34.         System.out.println("連接數[active:idle]-[" +  
    35.                        basicDataSource.getNumActive()+":"+basicDataSource.getNumIdle()+"]");  
    36.     }  
    37.   
    38.     public static void main(String[] args) {  
    39.         ApplicationContext ctx =   
    40.          new ClassPathXmlApplicationContext("com/baobaotao/connleak/applicatonContext.xml");  
    41.         JdbcUserService userService = (JdbcUserService) ctx.getBean("jdbcUserService");  
    42.   
    43.         BasicDataSource basicDataSource = (BasicDataSource) ctx.getBean("dataSource");  
    44.           
    45.         //④匯報數據源初始連接占用情況  
    46.         JdbcUserService.reportConn(basicDataSource);  
    47.   
    48.         JdbcUserService.asynchrLogon(userService, "tom");//啟動一個異常線程A  
    49.         JdbcUserService.sleep(500);  
    50.   
    51.         //⑤此時線程A正在執行JdbcUserService#logon()方法  
    52.         JdbcUserService.reportConn(basicDataSource);   
    53.   
    54.         JdbcUserService.sleep(2000);  
    55.   
    56.         //⑥此時線程A所執行的JdbcUserService#logon()方法已經執行完畢  
    57.         JdbcUserService.reportConn(basicDataSource);  
    58.   
    59.         JdbcUserService.asynchrLogon(userService, "john");//啟動一個異常線程B  
    60.         JdbcUserService.sleep(500);  
    61.   
    62.         //⑦此時線程B正在執行JdbcUserService#logon()方法  
    63.         JdbcUserService.reportConn(basicDataSource);  
    64.           
    65.         JdbcUserService.sleep(2000);  
    66.   
    67.         //⑧此時線程A和B都已完成JdbcUserService#logon()方法的執行  
    68.         JdbcUserService.reportConn(basicDataSource);  
    69.   
    70.     }  


        在JdbcUserService中添加一個可異步執行logon()方法的asynchrLogon()方法,我們通過異步執行logon()以及讓主線程睡眠的方式模擬多線程環境下的執行場景。在不同的執行點,通過reportConn()方法匯報數據源連接的占用情況。 
        通過Spring事務聲明,對JdbcUserServie的logon()方法進行事務增強,配置代碼如下所示: 
    Xml代碼  收藏代碼
    1. <?xml version="1.0" encoding="UTF-8" ?>  
    2. <beans xmlns="http://www.springframework.org/schema/beans"  
    3.       …  
    4.      http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">  
    5.     <context:component-scan base-package="com.baobaotao.connleak"/>  
    6.     <context:property-placeholder location="classpath:jdbc.properties"/>  
    7.     <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"  
    8.         destroy-method="close"   
    9.         p:driverClassName="${jdbc.driverClassName}"  
    10.         p:url="${jdbc.url}"   
    11.         p:username="${jdbc.username}"  
    12.         p:password="${jdbc.password}"/>  
    13.   
    14.     <bean id="jdbcTemplate"  
    15.           class="org.springframework.jdbc.core.JdbcTemplate"  
    16.           p:dataSource-ref="dataSource"/>  
    17.   
    18.     <bean id="transactionManager"  
    19.           class="org.springframework.jdbc.datasource.DataSourceTransactionManager"  
    20.           p:dataSource-ref="dataSource"/>  
    21.   
    22.      <!--①啟用注解驅動的事務增強-->  
    23.      <tx:annotation-driven/>        
    24. </beans>  

    然后,運行JdbcUserServie,在控制臺將觀察到如下的輸出信息: 
    引用
    連接數[active:idle]-[0:0] 
    連接數[active:idle]-[2:0] 
    連接數[active:idle]-[1:1] 
    連接數[active:idle]-[3:0] 
    連接數[active:idle]-[2:1]

    我們通過表10-3對數據源連接的占用和泄漏情況進行描述。 
    時間執行線程1執行線程2數據源連接
                   activeidleleak
    T0未啟動      未啟動000
    T1正在執行方法 未啟動200
    T2執行完畢  未啟動111
    T3執行完畢  正式執行方法301
    T4執行完畢  執行完畢212

       可見在執行線程1執行完畢后,只釋放了一個數據連接,還有一個數據連接處于active狀態,說明泄漏了一個連接。相似的,執行線程2執行完畢后,也泄漏了一個連接:原因是直接通過數據源獲取連接(jdbcTemplate.getDataSource().getConnection())而沒有顯式釋放。 

    通過DataSourceUtils獲取數據連接 

        Spring提供了一個能從當前事務上下文中獲取綁定的數據連接的工具類,那就是DataSourceUtils。Spring強調必須使用DataSourceUtils工具類獲取數據連接,Spring的JdbcTemplate內部也是通過DataSourceUtils來獲取連接的。    DataSourceUtils提供了若干獲取和釋放數據連接的靜態方法,說明如下: 

    •   static Connection doGetConnection(DataSource dataSource):首先嘗試從事務上下文中獲取連接,失敗后再從數據源獲取連接;
    • static Connection getConnection(DataSource dataSource):和doGetConnection方法的功能一樣,實際上,它內部就是調用doGetConnection方法獲取連接的;
    • static void doReleaseConnection(Connection con, DataSource dataSource):釋放連接,放回到連接池中;
    • static void releaseConnection(Connection con, DataSource dataSource):和doRelease Connection方法的功能一樣,實際上,它內部就是調用doReleaseConnection方法獲取連接的。


    來看一下DataSourceUtils從數據源獲取連接的關鍵代碼: 

    Java代碼  收藏代碼
    1. public abstract class DataSourceUtils {  
    2. …  
    3. public static Connection doGetConnection(DataSource dataSource) throws SQLException {  
    4.         Assert.notNull(dataSource, "No DataSource specified");  
    5.   
    6.      //①首先嘗試從事務同步管理器中獲取數據連接  
    7.     ConnectionHolder conHolder =   
    8.            (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);  
    9.     if (conHolder != null && (conHolder.hasConnection() ||   
    10.                           conHolder.isSynchronizedWithTransaction())) {   
    11.             conHolder.requested();  
    12.             if (!conHolder.hasConnection()) {  
    13.                 logger.debug("Fetching resumed JDBC Connection from DataSource");  
    14.                 conHolder.setConnection(dataSource.getConnection());  
    15.             }  
    16.             return conHolder.getConnection();  
    17.         }  
    18.   
    19.           //②如果獲取不到連接,則直接從數據源中獲取連接  
    20.         Connection con = dataSource.getConnection();  
    21.   
    22.          //③如果擁有事務上下文,則將連接綁定到事務上下文中  
    23.         if (TransactionSynchronizationManager.isSynchronizationActive()) {  
    24.               ConnectionHolder holderToUse = conHolder;  
    25.             if (holderToUse == null) {  
    26.                 holderToUse = new ConnectionHolder(con);  
    27.             }  
    28.             else {holderToUse.setConnection(con);}  
    29.             holderToUse.requested();  
    30.             TransactionSynchronizationManager.registerSynchronization(  
    31.                               new ConnectionSynchronization(holderToUse, dataSource));  
    32.             holderToUse.setSynchronizedWithTransaction(true);  
    33.             if (holderToUse != conHolder) {  
    34.                 TransactionSynchronizationManager.bindResource(  
    35.                                                             dataSource, holderToUse);  
    36.             }  
    37.         }  
    38.         return con;  
    39.     }  
    40.      …  
    41. }  


        它首先查看當前是否存在事務管理上下文,并嘗試從事務管理上下文獲取連接,如果獲取失敗,直接從數據源中獲取連接。在獲取連接后,如果當前擁有事務上下文,則將連接綁定到事務上下文中。 
        我們在JdbcUserService中,使用DataSourceUtils.getConnection()替換直接從數據源中獲取連接的代碼: 
    Java代碼  收藏代碼
    1. package com.baobaotao.connleak;  
    2. …  
    3. @Service("jdbcUserService")  
    4. public class JdbcUserService {  
    5.     @Autowired  
    6.     private JdbcTemplate jdbcTemplate;  
    7.   
    8.     @Transactional  
    9.     public void logon(String userName) {  
    10.         try {  
    11.             //①使用DataSourceUtils獲取數據連接  
    12.           Connection conn =   
    13.                  DataSourceUtils.getConnection(jdbcTemplate.getDataSource());  
    14.             //Connection conn = jdbcTemplate.getDataSource().getConnection();  
    15.   
    16.             String sql = "UPDATE t_user SET last_logon_time=? WHERE user_name =?";  
    17.             jdbcTemplate.update(sql, System.currentTimeMillis(), userName);  
    18.             Thread.sleep(1000);   
    19.         } catch (Exception e) {  
    20.             e.printStackTrace();  
    21.         }  
    22.     }  
    23. }  


        重新運行代碼,得到如下的執行結果: 

    引用
    連接數[active:idle]-[0:0] 
    連接數[active:idle]-[1:0] 
    連接數[active:idle]-[0:1] 
    連接數[active:idle]-[1:0] 
    連接數[active:idle]-[0:1]


       對照上一節的輸出日志,我們可以看到已經沒有連接泄漏的現象了。一個執行線程在運行JdbcUserService#logon()方法時,只占用一個連接,而且方法執行完畢后,該連接馬上釋放。這說明通過DataSourceUtils.getConnection()方法確實獲取了方法所在事務上下文綁定的那個連接,而不是像原來那樣從數據源中獲取一個新的連接。 

    通過DataSourceUtils獲取數據連接 

       是否使用DataSourceUtils獲取數據連接就可以高枕無憂了呢?理想很美好,但現實很殘酷:如果DataSourceUtils在沒有事務上下文的方法中使用getConnection()獲取連接,依然會造成數據連接泄漏! 
       保持上面的代碼不變,將上面Spring配置文件中①處的Spring AOP事務增強配置的代碼注釋掉,重新運行代碼清單10-23的代碼,將得到如下的輸出日志: 

    引用
    連接數[active:idle]-[0:0] 
    連接數[active:idle]-[1:1] 
    連接數[active:idle]-[1:1] 
    連接數[active:idle]-[2:1] 
    連接數[active:idle]-[2:1]


        我們通過下表對數據源連接的占用和泄漏情況進行描述。 
        仔細對上表的執行過程,我們發現在T1時,有事務上下文時的active為2,idle為0,而此時由于沒有事務管理,則active為1而idle也為1。這說明有事務上下文時,需要等到整個事務方法(即logon())返回后,事務上下文綁定的連接才被釋放。但在沒有事務上下文時,logon()調用JdbcTemplate執行完數據操作后,馬上就釋放連接。 

    時間執行線程1執行線程2數據源連接
                   activeidleleak
    T0未啟動未啟動000
    T1正在執行方法未啟動110
    T2執行完畢未啟動111
    T3執行完畢正式執行方法211
    T4執行完畢執行完畢212


       在T2執行線程完成logon()方法的調用后,有一個連接沒有被釋放(active),所以發生了連接泄漏。到T4時,兩個執行線程都完成了logon()方法的調用,但是出現了兩個未釋放的連接。 
       要堵上這個連接泄漏的漏洞,需要對logon()方法進行如下的改造: 
    Java代碼  收藏代碼
    1. package com.baobaotao.connleak;  
    2. …  
    3. @Service("jdbcUserService")  
    4. public class JdbcUserService {  
    5.     @Autowired  
    6.     private JdbcTemplate jdbcTemplate;  
    7.   
    8.     @Transactional  
    9.     public void logon(String userName) {  
    10.         try {  
    11.           Connection conn =   
    12.                  DataSourceUtils.getConnection(jdbcTemplate.getDataSource());  
    13.             String sql = "UPDATE t_user SET last_logon_time=? WHERE user_name =?";  
    14.             jdbcTemplate.update(sql, System.currentTimeMillis(), userName);  
    15.             Thread.sleep(1000);   
    16.             //①   
    17.         } catch (Exception e) {  
    18.             e.printStackTrace();  
    19.         }finally {  
    20.   
    21.             //②顯式使用DataSourceUtils釋放連接  
    22.             DataSourceUtils.releaseConnection(conn,jdbcTemplate.getDataSource());  
    23.         }  
    24.     }  
    25. }  

       在②處顯式調用DataSourceUtils.releaseConnection()方法釋放獲取的連接。特別需要指出的是:一定不能在①處釋放連接!因為如果logon()在獲取連接后,①處代碼前這段代碼執行時發生異常,則①處釋放連接的動作將得不到執行。這將是一個非常具有隱蔽性的連接泄漏的隱患點。 

    JdbcTemplate如何做到對連接泄漏的免疫 

         分析JdbcTemplate的代碼,我們可以清楚地看到它開放的每個數據操作方法,首先都使用DataSourceUtils獲取連接,在方法返回之前使用DataSourceUtils釋放連接。 
    來看一下JdbcTemplate最核心的一個數據操作方法execute(): 
    Java代碼  收藏代碼
    1. public <T> T execute(StatementCallback<T> action) throws DataAccessException {  
    2.   
    3.         //①首先根據DataSourceUtils獲取數據連接  
    4.          Connection con = DataSourceUtils.getConnection(getDataSource());  
    5.         Statement stmt = null;  
    6.         try {  
    7.             Connection conToUse = con;  
    8.             …  
    9.             handleWarnings(stmt);  
    10.             return result;  
    11.         }  
    12.         catch (SQLException ex) {  
    13.             JdbcUtils.closeStatement(stmt);  
    14.             stmt = null;  
    15.   
    16.               //②發生異常時,使用DataSourceUtils釋放數據連接  
    17.               DataSourceUtils.releaseConnection(con, getDataSource());  
    18.             con = null;  
    19.             throw getExceptionTranslator().translate(  
    20.                                 "StatementCallback", getSql(action), ex);  
    21.         }  
    22.         finally {  
    23.             JdbcUtils.closeStatement(stmt);  
    24.                 
    25.               //③最后再使用DataSourceUtils釋放數據連接  
    26.             DataSourceUtils.releaseConnection(con, getDataSource());  
    27.         }  
    28.     }  


        在①處通過DataSourceUtils.getConnection()獲取連接,在②和③處通過DataSourceUtils.releaseConnection()釋放連接。所有JdbcTemplate開放的數據訪問API最終都是直接或間接由execute(StatementCallback<T> action)方法執行數據訪問操作的,因此這個方法代表了JdbcTemplate數據操作的最終實現方式。 
        正是因為JdbcTemplate嚴謹的獲取連接及釋放連接的模式化流程保證了JdbcTemplate對數據連接泄漏問題的免疫性。所以,如有可能盡量使用JdbcTemplate、HibernateTemplate等這些模板進行數據訪問操作,避免直接獲取數據連接的操作。 

    使用TransactionAwareDataSourceProxy 


        如果不得已要顯式獲取數據連接,除了使用DataSourceUtils獲取事務上下文綁定的連接外,還可以通過TransactionAwareDataSourceProxy對數據源進行代理。數據源對象被代理后就具有了事務上下文感知的能力,通過代理數據源的getConnection()方法獲取連接和使用DataSourceUtils.getConnection()獲取連接的效果是一樣的。 
       下面是使用TransactionAwareDataSourceProxy對數據源進行代理的配置: 

    Xml代碼  收藏代碼
    1. <?xml version="1.0" encoding="UTF-8" ?>  
    2. <beans xmlns="http://www.springframework.org/schema/beans"  
    3.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    4.        …  
    5.        http://www.springframework.org/schema/tx      
    6.        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">  
    7.     <context:component-scan base-package="com.baobaotao.connleak"/>  
    8.     <context:property-placeholder location="classpath:jdbc.properties"/>  
    9.   
    10.     <!--①未被代理的數據源 -->  
    11.     <bean id="originDataSource" class="org.apache.commons.dbcp.BasicDataSource"  
    12.         destroy-method="close"   
    13.         p:driverClassName="${jdbc.driverClassName}"  
    14.         p:url="${jdbc.url}"   
    15.         p:username="${jdbc.username}"  
    16.         p:password="${jdbc.password}"/>  
    17.       
    18.      <!--②對數據源進行代碼,使數據源具體事務上下文感知性 -->  
    19.     <bean id="dataSource"  
    20.         class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy"  
    21.         p:targetDataSource-ref="originDataSource" />  
    22.   
    23.     <bean id="jdbcTemplate"  
    24.           class="org.springframework.jdbc.core.JdbcTemplate"  
    25.           p:dataSource-ref="dataSource"/>  
    26.   
    27.     <bean id="transactionManager"  
    28.           class="org.springframework.jdbc.datasource.DataSourceTransactionManager"  
    29.           p:dataSource-ref="dataSource"/>           
    30.     <tx:annotation-driven/>        
    31. </beans>  


        對數據源進行代理后,我們就可以通過數據源代理對象的getConnection()獲取事務上下文中綁定的數據連接了。因此,如果數據源已經進行了   TransactionAwareDataSourceProxy的代理,而且方法存在事務上下文,那么代碼清單10-19的代碼也不會生產連接泄漏的問題。 

    其他數據訪問技術的等價類 

        理解了Spring JDBC的數據連接泄漏問題,其中的道理可以平滑地推廣到其他框架中去。Spring為每個數據訪問技術框架都提供了一個獲取事務上下文綁定的數據連接(或其衍生品)的工具類和數據源(或其衍生品)的代理類。 
       表10-5列出了不同數據訪問技術對應DataSourceUtils的等價類。 
    表10-5  不同數據訪問框架DataSourceUtils的等價類 
    數據訪問技術框架連接(或衍生品)獲取工具類                               
    Spring JDBCorg.springframework.jdbc.datasource.DataSourceUtils      
    Hibernateorg.springframework.orm.hibernate3.SessionFactoryUtils   
    iBatisorg.springframework.jdbc.datasource.DataSourceUtils      
    JPAorg.springframework.orm.jpa.EntityManagerFactoryUtils    
    JDOorg.springframework.orm.jdo.PersistenceManagerFactoryUtils


      表10-6列出了不同數據訪問技術框架下TransactionAwareDataSourceProxy的等價類。 
    表10-6  不同數據訪問框架TransactionAwareDataSourceProxy的等價類 
    數據訪問技術框架連接(或衍生品)獲取工具類                                                    
    Spring JDBCorg.springframework.jdbc.datasource.TransactionAwareDataSourceProxy       
    Hibernateorg.springframework.orm.hibernate3.LocalSessionFactoryBean                    
    iBatisorg.springframework.jdbc.datasource.TransactionAwareDataSourceProxy           
    JPA無                                                                        
    JDOorg.springframework.orm.jdo.TransactionAwarePersistenceManagerFactoryProxy


    注:以上內容摘自《Spring 3.x企業應用開發實戰》
    分享到:  
    評論
    2 樓 huang_yong 2012-04-14  
    寫程序的時候,如果是從底層獲取Connection,一定要注意在finally里面close掉,但是如果從Hibernate的SessionFactory級別獲取Connection,就無需手工close掉,這些工作都由Hibernate為我們完成了。

    一定要使用:

    Connection conn = SessionFactoryUtils.getDataSource(hibernateTemplate.getSessionFactory()).getConnection();

    獲取Connection,以上代碼可以進行封裝,放入公共類中,方便其他程序員使用。
    1 樓 jinnianshilongnian 2012-03-09  
    1、打開連接不關閉 這是程序員犯的不該犯的錯誤(發現錯誤后可以改)

    2、當有事務方法 特別慢,會拖慢整個應用 甚至造成死鎖。
        以前我們用c3p0曾經遇到過類似的,在用戶注冊高峰時,由于贈送積分/還有一些道具之類的是和另一個系統集成的,所以在高峰期特別慢,從而導致整個注冊方法特別慢,最后改成異步贈送(失敗了影響也是比較小的)。

    因此第一種情況,是可以查找并改正的。
    第二種情況,需要實際情況實際分析。

    posted on 2013-05-29 09:53 brock 閱讀(1019) 評論(0)  編輯  收藏 所屬分類: oracle 數據庫
    主站蜘蛛池模板: 国产网站在线免费观看| 337P日本欧洲亚洲大胆艺术图| 亚洲人成中文字幕在线观看| 亚洲A∨午夜成人片精品网站| 国产福利免费观看| 免费国产成人午夜电影| 国产精品视频免费一区二区三区| 永久免费AV无码网站在线观看 | 精品国产日韩亚洲一区91| 亚洲JLZZJLZZ少妇| 午夜亚洲国产精品福利| 麻豆69堂免费视频| 一级做a免费视频观看网站| 九九99热免费最新版| 在线观看免费视频一区| 免费精品一区二区三区第35| 人妻无码久久一区二区三区免费| 69av免费视频| 四虎影院免费视频| 亚洲 国产 图片| 亚洲精品无码久久千人斩| 久久国产亚洲高清观看| 亚洲av日韩av无码av| 色欲色欲天天天www亚洲伊| 一级毛片免费在线| 久久永久免费人妻精品| 国内精品乱码卡1卡2卡3免费| 免费高清小黄站在线观看 | 99久久免费中文字幕精品| 曰曰鲁夜夜免费播放视频 | 午夜dj在线观看免费视频| 亚洲高清成人一区二区三区| 国产亚洲真人做受在线观看| 亚洲高清免费在线观看| 亚洲色成人WWW永久在线观看| 特黄特色的大片观看免费视频| a级片免费观看视频| 国国内清清草原免费视频99| 在线a亚洲v天堂网2018| 久久亚洲AV无码精品色午夜麻| 亚洲国产91在线|