如何避免并发数据库查询的大延迟?

时间:2013-07-08 08:49:36

标签: java oracle apache jdbc cgi

我正在编写一个小程序,它将通过CGI在Apache Web服务器(而不是Tomcat)上启动,以响应POST请求。

该计划执行以下操作:

  1. 读取xml,通过请求中的http发送
  2. 使用从xml
  3. 中提取的数据在数据库中执行存储过程
  4. 返回存储过程的结果作为对POST请求的响应
  5. 数据库是Oracle。我使用jdbc OCI来访问它。

    Class.forName("oracle.jdbc.OracleDriver");
    
    String dbCS = "jdbc:oracle:oci:@//ip:port/service"
    
    Connection conn = DriverManager.getConnection(dbCS, dbUserId, dbPwd);
    CallableStatement cs = conn.prepareCall("{ call ? := my_pkg.my_sp(?,?,?,?)}");
    cs.registerOutParameter(pReturnValue, OracleTypes.NUMBER);
    cs.setInt("p1", p1);
    cs.setString("p2", p2);
    cs.setString("p3", p3);
    cs.registerOutParameter("p_out", Types.VARCHAR);
    try {
        cs.executeQuery();
        return cs.getString(pReqResponse);
    } finally {
        try {
            cs.close();
       } catch (SQLException ex) {
            //....        
       }
    }
    

    在执行单个请求时,它工作正常(整个程序在2秒内完成)。但是,如果我尝试一次发送多个POST请求,我会将所有这些卡住一段时间,具体取决于请求的数量(对于10个请求,大约是10秒。 15秒,15请求。)

    我试图估计,代码的哪一部分给出了延迟。它似乎有两行:

    Connection conn = DriverManager.getConnection(dbConnectionString, dbUserId, dbPwd);
    CallableStatement cs = conn.prepareCall("{ call ? := my_pkg.my_sp(?,?,?,?)}");
    

    执行本身几乎立即完成。

    为什么会这样?

    P.S。:我在Windows7上进行了相同的实验。当然,它不是从Web服务器启动的,而是一个简单的控制台进程。它还必须从硬盘驱动器上的文件中读取xml。所有并发启动的程序实例都在一起完成。

    是什么阻止它通过Apache在Linux上快速运行?


    基于评论

    我尝试为我的连接设置泳池属性,但都是徒劳的。我尝试了以下方法:

    1. 在网址

      中指定UserId和密码
      jdbc:oracle:oci:login/password@//ip:port/service
      

      我尝试设置连接属性:

       Properties p = new Properties();
       p.setProperty("Pooling", "true");
       p.setProperty("Min Pool Size", "1");
       p.setProperty("Max Pool Size", "10");
       p.setProperty("Incr Pool Size", "4");
      
       Connection conn = DriverManager.getConnection(dbConnectionString, p);
      
    2. 我尝试使用OCI Connection Pooling

       OracleOCIConnectionPool cpool = new OracleOCIConnectionPool();
       cpool.setUser("user");
       cpool.setPassword("pwd");
       cpool.setURL(dbConnectionString);
      
       Properties p = new Properties();
       p.put(OracleOCIConnectionPool.CONNPOOL_MIN_LIMIT, "1");
       p.put(OracleOCIConnectionPool.CONNPOOL_MAX_LIMIT, "5");
       p.put(OracleOCIConnectionPool.CONNPOOL_INCREMENT, "2");
       p.put(OracleOCIConnectionPool.CONNPOOL_TIMEOUT, "10");
       p.put(OracleOCIConnectionPool.CONNPOOL_NOWAIT, "true");
       cpool.setPoolConfig(p);
      
       Connection conn = (OracleOCIConnection) cpool.getConnection();
      
    3. 我尝试使用apache DBCP component

      basicDataSource = new BasicDataSource();
      basicDataSource.setUsername("user");
      basicDataSource.setPassword("pwd");
      basicDataSource.setDriverClassName("oracle.jdbc.OracleDriver");
      basicDataSource.setUrl(dbConnectionString);
      
      Connection conn = basicDataSource.getConnection();
      
    4. 行为保持不变,即所有并发请求中getConnection的延迟很大。

      所有这些尝试似乎都试图解决其他问题,因为在我的情况下,所有连接都是从不同的进程建立的,并且在不同进程之间管理来自一个池的连接看起来不明显(我在这里弄错了?)

      我有哪些选择?或者我可能做错了什么? 另外我应该说,我对java很新,所以我可能会遗漏一些基本的东西..


      这可能是操作系统或网络服务器问题吗?可能应该在那里设置一些东西,而不是代码......?


      此外,我尝试使用thin客户端而不是oci。然而它更奇怪地工作:第一个请求在一秒钟内完成,而第二个请求延迟了分钟


      Poor concurrency with Oracle JDBC drivers陈述了与我类似的问题。


      最后,我们发现Apache通过CGI启动的进程占用了所有100%的CPU(以及大部分内存),因此他们根本没有足够的资源。不幸的是,我不知道,为什么一个非常简单和基本的程序(读取xml并建立一个与DB的连接以执行存储过程)仅启动了20次,只吃了所有资源。

      然而,解决方案确实非常明显。我已经使用servlet将它重构为一个java web应用程序,我们将它部署在Apache Tomcat和MAGIC上......它开始按预期工作,对资源没有任何明显的影响。

1 个答案:

答案 0 :(得分:0)

我认为问题出在cgi上。当你发出cgi请求时,它会启动一个新的cpu进程来处理请求。每个新请求也在新的JVM中,因此连接池不是一个选项。

即便如此,获得连接也应该比这更快。也许在Oracle本身有配置选项可以控制你可以拥有的并发连接数,但我不是Oracle专家。