小弟在编一个查询数据库信息的类,原先的SQL语句是一个union查询,即把很多的select语句用union标识符连接起来,以便可以一次性从数据库检索到信息。不过由于数据量太大,union查询要很长时间才出结果,因而网页上面显示查询到的数据也要等待很长时间。于是我打算把每个查询放到一个线程中去跑,但是最终出来查询结果的总时间和用union查询出来的总时间差不多,我想用线程查询的话结果应该会快一点。不知道是不是代码写的有问题,还是oracle数据库会对从一台机子过来的查询请求进行排队。希望各位老大指点一二。代码如下:
try{
       catchConn(); //得到数据库连接
       outputList=new Vector();
       outputArrayList=new ArrayList();
       class Query implements Runnable {
          private String innersql;
          Thread t;          private Thread gett(){
              return t;
           }          public Query(String sqls){
             innersql=sqls;
             t=new Thread(this);
             t.start();
          }
 
         public void run(){
           try{
               Statement innerstmt = dbConn.createStatement    (java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE,java.sql.ResultSet.CONCUR_READ_ONLY);
               ResultSet innerrs = innerstmt.executeQuery(innersql);
               while (innerrs.next()){
                  Hashtable outputInfoList = new Hashtable();
                  outputInfoList.put("name",innerrs.getString(1));
                  outputInfoList.put("area",innerrs.getString(2));
                  outputInfoList.put("count",new Integer(innerrs.getInt(3)));
                  System.out.println(innerrs.getString(1)+innerrs.getString(2)+"  "+innerrs.getString(3));
                  outputArrayList.add(outputInfoList);
                }
            innerrs.close();
            innerstmt.close();
           }
           catch(Exception e1){
                System.out.println("error");
           }
        }
     }
       Query a1=new Query(sqllist[0]);  
      //sqllist是7个长度的一维数组,每一个单位里面放一条查询语句,就是原来准备用union查询来合并的查询语句
       Query a2=new Query(sqllist[1]);
       Query a3=new Query(sqllist[2]);
       Query a4=new Query(sqllist[3]);
       Query a5=new Query(sqllist[4]);
       Query a6=new Query(sqllist[5]);
       Query a7=new Query(sqllist[6]);
   try{
        a1.gett().join();
        a2.gett().join();
        a3.gett().join();
        a4.gett().join();
        a5.gett().join();
        a6.gett().join();
        a7.gett().join();
      }
  catch(Exception e){
  }
  接下来的语句不贴了,就是等到所有线程运行完毕后,把查询结果从ArrayList取出来,显示在网页上。

解决方案 »

  1.   

    速度瓶颈在于Oracle而非Java,就算你开100个线程还是一样样等待Oracle的结果集返回,想办法去优化你的SQL吧,这比搞什么Java多线程有效得多。
      

  2.   

    你这么做没必要,而且Oracle查找的时候是行锁定的.....
      

  3.   

    to wingtrace(无心·无梦·无语):
       我还是没明白你的意思,有没有什么资料让我看看,我想一个查询完毕后,它的结果集就会返回,难道是等到所有查询线程查询完毕再把结果集返回?
      

  4.   

    非也,具体加不加行所需要根据事务隔离级别而定,一般来说oracle对于select是不加锁的,增删改的时候才加行级或表级锁。如果事务隔离级别严格到对select都要加行锁,那么数据库的执行效率可想而知,到底事务级别应该如何决策应该依具体业务需要而定,并非越严格越好或者越放松越好。
      

  5.   

    to  Squall1009(钰枫)(找工作ing) :
         行锁定是什么意思,有没有介绍资料?我试了一下,如果两个select语句同时并发查询一个表的话,会比单个select语句执行出来结果要慢,说明同时查询一个表会影响查询效果,但是比两个select语句分开顺序查询时间要快,说明并发是有效的。我并非要做到查询效果很高效,只不过想速度稍微快一点,也不需要什么数据库服务器端编程,这个是单位里面用来查询数据用。
      

  6.   

    我现在想了解的是是否代码写的有问题,还是oracle自身的问题,另外最好能有个稍微详细点的解释,拜托诸位。
      

  7.   

    以下是引用大富翁上面的讨论,欢迎大家就这个问题讨论一下:
    来自:yanyandt2, 时间:2005-4-27 21:16:42, ID:3059377
    你用数据库连接池试试看?  
    来自:xiaqiapeng, 时间:2005-4-27 21:36:47, ID:3059389 | 编辑
    to yanyandt2:
       连接池我没用过,是不是把几个数据库连接放到一个vector里面,然后要用的时候取出一个去查询还是把几个查询线程放到一个vector里面,然后要用的时候拿出一个来查询?我有点忘了,这个对我来说有点难。  
    来自:xiaqiapeng, 时间:2005-4-27 22:43:45, ID:3059478 | 编辑
    快来救人啊  
    来自:xiaqiapeng, 时间:2005-4-28 8:06:27, ID:3059594 | 编辑
    今天总有人来解答了吧  
    来自:yanyandt2, 时间:2005-4-28 15:32:28, ID:3060219
    啊,连接池就是在服务器端配置的管理数据库连接的,
    welogic,tomcat里都能配置  
    来自:lover402, 时间:2005-4-28 16:08:59, ID:3060272
    晕倒!web请求方式是一应一答,发送HTTP请求,想要返回查询结果,必然是要等待的!!
    这和你用不用线程有什么关系??再说了,servlet本身就支持多线程方式  
    来自:xiaqiapeng, 时间:2005-4-28 22:12:44, ID:3060575 | 编辑
    我的意思是想把union查询里面的7个select语句拆开放到7个线程中去跑,按照理论,出来的查询结果应该比union查询出来的结果快,但是事与愿违,出来结果的时间和union查询出来的时间一样,我就感到不能理解.  
    来自:yanyandt2, 时间:2005-4-28 23:16:03, ID:3060605
    你虽然用了7个线程,但是数据库连接还是一个,所以时间就一样了。  
    来自:wpx, 时间:2005-4-29 12:20:25, ID:3060626
    把你要连接的表先写个VIEW,然后在VIEW里查试试  
    来自:yangxiao_jiang, 时间:2005-4-29 16:12:20, ID:3060899
    wpx说的对,你可以建立视图,或者写存储过程,这样应该能快一些
    而且,你查询用不用线程都是一样的。楼上的都说了,用连接池只能去掉连接数据库的开销,查询的时间是少不了得。  
    来自:lares, 时间:2005-4-29 16:26:42, ID:3060916
    用多线程不会加快速度的,用union快  
    来自:kusanagi, 时间:2005-4-29 17:18:47, ID:3061016
    //用多线程不会加快速度的,用union快
    严重表示同意,多线程会使速度更慢。
    你把代码贴出来看看。
    如果能不用union的话会快一些,用了也就是7次查询时间+1次连接数据库时间  
    来自:xiaqiapeng, 时间:2005-4-30 0:20:47, ID:3061278 | 编辑
    to yanyandt2:
       引用:" 来自:yanyandt2, 时间:2005-4-28 23:16:03, ID:3060605
       你虽然用了7个线程,但是数据库连接还是一个,所以时间就一样了。  "
       什么意思??如果多几个数据库连接,速度会快?!各位老大,我其实也是想试试多线程能否奏效,我刚在学,拜托![:D]      
    来自:xiaqiapeng, 时间:2005-4-30 0:08:31, ID:3061282 | 编辑
    我知道union的时间和一次一次查的时间差不多,这个贴子想讨论的就是为何用7个线程查询出来的时间和union查询出来的时间差不多,请往这方面讨论.yanyandt2已经点到了一点,各位知情的能否再深入探讨一下,本人也是个学习JAVA的新手,想做深入的了解.[:D]  
    来自:xiaqiapeng, 时间:2005-4-30 0:19:34, ID:3061292 | 编辑
    好了,说的再具体一点吧,这个贴子就是讨论本例中多线程的功效,我对我的union查询做了测试,运行一次要14秒(最近的一次竟然运行了36秒,数据库业务忙的时候),单个select子查询要2秒(有7个这样的子查询),我原先考虑的是我大概能在3-4秒出结果(不管我预测是否对还是错,因为具体的调度策略我不知道,但是也应该比14秒要快吧?!),结果出来的还是和union查询一样的时间,我就是这点不明白,大家讨论讨论吧,暂不考虑服务器端编程的问题,当然也欢迎大家谈[:D]  
    来自:yanyandt2, 时间:2005-4-30 9:17:37, ID:3061412
    你用了多线程,其实线程是开在了java虚拟机中,而7个线程都用了一个数据库连接,
    所以总的来说,你在数据库端的访问还是 1 个“通道”。
    我是建议你每个线程使用不同的连接,再试试。  
     
    问题讨论没有结束 ... 
      

  8.   

    yanyandt2说的比较正确,
    你的多线程共享一个connection,而一个connection链接一个数据库,当然一次只能执行一条SQL语句,这和没有多线程的效率是一样的,你程序的瓶颈在connection。
    建议你把线程类从方法中拿出来,单独定义。
      

  9.   

    晕哦,在大富翁上都给你说了。
    多线程提高的只是cpu利用率。比如单线程占用10%CPU资源。那么拥有7线程的进程相当于占用了70%的cpu资源。
    但是你如果每个线程里进行数据库查询。数据库执行查询时内部进行复杂的计算占用90%的的cpu资源,那么就没有多少cpu资源可用了。怎么会有效率的提高呢?
    多线程只是在cpu空闲时有用!
    用view是没多大用的(少了sql语句编译的一点点时间),用存储过程更是瞎扯。union的效率绝对比多线程高。
    想要再提高效率,只能在SQL语句本身进行优化,或者想办法不用union.
    楼上所说的每线程都开一个数据库连接的方式也许可以,不知道数据库内部如何处理,不敢妄加评论
      

  10.   

    试过了,每个线程都建了一个connection,还是不行,以下是大富翁上面的讨论:
    ---------------------------------------------------------------------------来自:xiaqiapeng, 时间:2005-4-30 10:10:45, ID:3061470 | 编辑
    好的  
    来自:xiaqiapeng, 时间:2005-4-30 13:51:27, ID:3061682 | 编辑
    试了一下,还是老样(14秒),而且控制台输出是到14秒的时候一下子都出来的,而不是随着时间交替输出,感觉还是没有并发,以下是修改后的代码:
    private Connection getConn(){
    try {
          Context initCtx = new InitialContext();
          Context ctx = (Context) initCtx.lookup("java:comp/env"); //获取连接池对象
          Object obj = (Object) ctx.lookup("jdbc/OracleDB"); //类型转换
          javax.sql.DataSource ds = (javax.sql.DataSource) obj;
          return ds.getConnection();
       }
        catch (Exception sqle) {
          System.out.println("cannot connect db inside getConn");
          return null;
        }
    } class Query implements Runnable {
     private String innersql;
     Thread t;
     private Thread gett(){
     return t;
    }
     public Query(String sqls){
        innersql=sqls;
        t=new Thread(this);
        t.start();
     }
    public void run(){
        try{
          Connection innerconn=getConn();
          Statement innerstmt = innerconn.createStatement    (java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE,java.sql.ResultSet.CONCUR_READ_ONLY);
          ResultSet innerrs = innerstmt.executeQuery(innersql);
          while (innerrs.next()){
            Hashtable outputInfoList = new Hashtable();
            outputInfoList.put("name",innerrs.getString(1));
            outputInfoList.put("area",innerrs.getString(2));
            outputInfoList.put("count",new Integer(innerrs.getInt(3)));
            System.out.println(innerrs.getString(1)+innerrs.getString(2)+"  "+innerrs.getString(3));
            outputArrayList.add(outputInfoList);
           }
          innerrs.close();
          innerstmt.close();
          innerconn.close();      
         }
        catch(Exception e1){
           System.out.println("error");
          }
        }
      }  
    以下是配置:
    C:\Tomcat 4.1\conf\server.xml
        <!-- Tomcat Venture Context -->
            <Context path="/venture" docBase="venture" debug="0">
              <Resource name="jdbc/OracleDB" auth="Container"
              type="javax.sql.DataSource"/>
              <ResourceParams name="jdbc/OracleDB">
               <parameter>
              <name>factory</name>
              <value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
                </parameter>
                <parameter>
              <name>driverClassName</name>
              <value>oracle.jdbc.driver.OracleDriver</value>
                </parameter>
                <parameter>
              <name>url</name>
              <value>jdbc:oracle:thin:@130.37.0.1:1521:oranb</value>
                </parameter>
                <parameter>
              <name>username</name>
              <value>test</value>
                </parameter>
                <parameter>
              <name>password</name>
              <value>test</value>
                </parameter>
                <parameter>
              <name>maxActive</name>
              <value>20</value>
                </parameter>
                <parameter>
              <name>maxIdle</name>
              <value>10</value>
               </parameter>
                <parameter>
              <name>maxWait</name>
              <value>5000</value>
                </parameter>
              </ResourceParams>
           </Context>
    [:D]
      
    来自:xiaqiapeng, 时间:2005-4-30 13:33:13, ID:3061690 | 编辑
    我只修改了线程类,加了个得到数据库连接的方法,就是每次建立一个新的数据库连接,先试试,也不搞复杂的连接池了,其他和原来一样。[:D]  
     
    问题讨论没有结束 ... 
      

  11.   

    以下为问题的连接地址
    http://www.delphibbs.com/delphibbs/dispq.asp?lid=3059373
      

  12.   

    select 'a887',area,count(*) from venture.userinfo where (yhlb='CA' or yhlb='CE') and logintype<>'KH121' and to_char(logdt,'YYYYMMDD') between '19900101' and '20050327' group by area 
    union 
    select 'b112',area,count(*) from venture.userinfo where yhlb='KG' and logintype<>'KH120' and to_char(logdt,'YYYYMMDD') between '19900101' and '20050327' group by area 
    union 
    select 'c174',area,count(*) from venture.userinfo where yhlb='DG' and logintype<>'KH113' and to_char(logdt,'YYYYMMDD') between '19900101' and '20050327' group by area 
    union 
    select 'd027',area,count(*) from venture.userinfo where (yhlb='XH' or yhlb='XE') and logintype<>'KH119' and to_char(logdt,'YYYYMMDD') between '19900101' and '20050327' group by area 
    union 
    select 'e034',area,count(*) from venture.userinfo where yhlb='ZH' and logintype<>'KH120' and to_char(logdt,'YYYYMMDD') between '19900101' and '20050327' group by area 
    union 
    select 'f103',area,count(*) from venture.userinfo where yhlb='KG' and yhlb2='LYH' and logintype<>'KH120' and to_char(logdt,'YYYYMMDD') between '19900101' and '20050327' group by area 
    union 
    select 'g010',area,count(*) from venture.userinfo where yhlb='XM' and yhlb2='LYH' and logintype<>'KH119' and to_char(logdt,'YYYYMMDD') between '19900101' and '20050327' group by area;
      

  13.   

    优化你的SQL语句,多线程没有用的
      

  14.   

    SQL语句需要优化,首先要在条件判断的字段上加上语句,第二是要在条件判断的时候避免对字段的值进行操作,就是说,你不应该将你的时间字段转换成字符串进行比较,而是把具体的值转换成时间类型进行比较。
      

  15.   

    select field1,area,count(*) as shuliang from (
    select 
    case when (yhlb='CA' or yhlb='CE') and logintype<>'KH121' then 'a887' 
    when yhlb='KG' and logintype<>'KH120' then 'b112'
    when yhlb='DG' and logintype<>'KH113' then 'c174'
    else ......
    end case as field1
    ,area from venture.userinfo
    where to_char(logdt,'YYYYMMDD') between '19900101' and '20050327'
    ) as temptable
    group by field1,area
    早贴出来不久结了嘛,一个sql搞定
    7个union相当于内部查询7次然后汇总成结果集。这个sql是我按sqlserver2000的写法写的。
    你看看oracle中case的写法和子查询的写法。另外to_char(logdt,'YYYYMMDD') between '19900101' and '20050327'也是一个效率低下的原因
    如果你对logdt作了索引,这种写法是不会利用到索引检索的。因为你对该字段进行了一次计算再比较。如果把'19900101'改成日期类型用大于小于符号比较就能提高效率。
    完毕!
      

  16.   

    举个例子:
    select 'a887',area,count(*) from venture.userinfo where (yhlb='CA' or yhlb='CE') and logintype<>'KH121' and logdt between to_date('19900101','YYYYMMDD') and to_date('20050327','YYYYMMDD') group by area
      

  17.   

    这个贴子想讨论的就是为何用7个线程查询出来的时间和union查询出来的时间差不多,请往这方面讨论说的再具体一点吧,这个贴子就是讨论本例中多线程的功效,我对我的union查询做了测试,运行一次要14秒,单个select子查询要2秒(有7个这样的子查询),我原先考虑的是我大概能在3-4秒出结果(不管我预测是否对还是错,因为具体的调度策略我不知道,但是也应该比14秒要快吧?!),结果出来的还是和union查询一样的时间,我就是这点不明白,大家讨论讨论吧,暂不考虑服务器端编程的问题.
      

  18.   

    服务器端SQL代码的确没有优化过。我以后要在SQL方面多下功夫.目前请大家讨论多线程的问题。
      

  19.   

    怎么就跟你说不明白呢?举例来说吧。
    每个子查询2秒=0.5秒建立连接+1.5秒进行查询。
    1.5秒进行查询这个事件是一个大计算量任务,cpu没有空闲(不能切换到其它线程或切换后也没有意义)。
    7个连接的最好情况是0.5+1.5*7=11秒,这是建立在所有建立连接的工作并行完成的前提下。
    多线程在进行多个查询时(多个1.5秒的计算任务可能会有并行,但由于是大计算量任务并行也无法更高的提高cpu利用率来提高速度,而提高cpu利用率是多线程提速的途径)时可能有些优势,但在多次建立连接的开销又把它抹平了。
      

  20.   

    你讲的有道理,但是另外我也感到疑惑,因为我有测试过,比如一个union查询要24秒,我将两个union查询在两台机子上同时并发运行,结果他们出来查询结果的时间同是36秒,就是说我如果顺序查询的话要2个24秒,但是我同时执行的话只要36秒,节省了24*2-36=12秒.我想oracle数据库应该是会并发来运行用户查询的(通过某种特殊的调度策略),至少不是顺序的.
      

  21.   

    我现在的情况就是疑惑.转贴O'REILLY出版的Java线程(第二版)中的一段话印证vincege(热得快)的观点.假设一个计算服务器(CalcServer)对于每一个客户的连接都会进行某种复杂的分析计算工作;同样假设这个计算对于每一个客户要耗时5秒钟.当有五个用户同时连接到服务器上时,CalcServer就会创建五个单独的线程.如果这些线程是分时间片的,它们要使用25秒来完成所有的计算工作,因为这五个线程是被平等对待的.我们可以认为这五个线程都是大概在25秒后才完成它们各自的计算任务的.因此每一个客户都是在25秒后才得到其计算结果的.如果我们的CalcServer不使用循环调度算法,就会有不同的结果:客户还是在同一时刻连接上服务器,但是某个客户(可能是任意选择的一个)得到了完成其计算工作的机会;因此第一个客户在第5秒就得到了结果,而不是25秒.接着第二个客户的计算开始了;第二个客户会在第10秒得到结果,依此类推.只有第5个客户会在第25秒得到运算结果.到底上面的哪个调度机制更合理些?答案依赖于在这5秒钟内服务器的行为.如果仅仅是输出一个结果,很显然非时间片调度算法更公平:平均而言,每一个客户要等待15秒来得到结果而不是时间片算法中的25秒.但是如果服务器在每一秒钟的计算中都会给客户一个结果的话,则时间片算法更公平:每一个客户在5秒钟后都有一个结果,而在非时间片算法中,第五个客户到了第21秒钟才会得到第一个结果。换句话说,对于“显示中间结果”这样的要求而言,循环调度算法提供了公平的结果。但是如果我们所关心的是最终的计算答案,那么在一个单CPU的机器上循环调度算法并不合适:即使在最好的情况下,它也没有体现出什么优越性,而对于CalcServer这样的例子而言,它实际上是降低了系统的吞吐量。对于一个有多个CPU的系统而言,情况就更复杂了。如果在一个有四个CPU的机器上运行五个线程,假设系统不是使用循环调度算法,客户得到结果的平均时间是6秒钟:头四个将在第5秒钟得到结果,而最后一个会在第10秒得到结果。另一方面,如果使用循环调度算法,平均响应时间是6.2秒。但是,每一个回答的时间都非常的接近于6.2秒钟:实际上,我们可以认为每一个客户都会在6.2秒钟后得到结果。因此,尽管循环调度算法的平均计算时间稍微多一点,但是它提供了更高的公平性。而且在这个例子中,如果我们最关心的是何时得到这五个线程的最终结果,则循环调度算法更快:6.2秒对10秒。