注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

和申的个人主页

专注于java开发,1985wanggang

 
 
 

日志

 
 

JBOSS连接池调优5-合理的设置fetchsize  

2012-01-05 14:23:55|  分类: java调优 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

在前面的几节中,我们经常会提到一个词“Fetchsize”,并且在《JBOSS连接池调优2-合理的设置PreparedStatementCache》一节中研究了fetchsize对应用内存的影响。网上关于fetchsize的文章不是很多,很难对这个参数有一个全面的了解,确实,如果从开发的角度来理解fetchsize,确实是存在一些困难的。那么到时什么是fetchsize?fetchsize对内存和性能到底有什么影响呢?我在前段日子作了不少测试和研究,下面将自己的研究成果和大家分享一下。

1. 什么是fetchsize?

1.1  Oracle中的fetchsize

先来简单解释一下,当我们执行一个SQL查询语句的时候,需要在客户端和服务器端都打开一个游标,并且分别申请一块内存空间,作为存放查询的数据的一个缓冲区。这块内存区,存放多少条数据就由fetchsize来决定,同时每次网络包会传送fetchsize条记录到客户端。应该很容易理解,如果fetchsize设置为20,当我们从服务器端查询数据往客户端传送时,每次可以传送20条数据,但是两端分别需要20条数据的内存空闲来保存这些数据。fetchsize决定了每批次可以传输的记录条数,但同时,也决定了内存的大小。这块内存,在oracle服务器端是动态分配的(大家可以想想为什么)。而在客户端(JBOSS),PS对象会存在一个缓冲中(LRU链表),也就是说,这块内存是事先配好的,应用端内存的分配在conn.prepareStatement(sql)或都conn.CreateStatement(sql)的时候完成。

在java程序中,我们会执行以下代码:

//打开游标,执行查询,但是并不获取任何的数据,网络上没有数据的传输。 rs = stmt.executeQuery(); //获取具体的数据,网络一般每次传输fetchsize条数据。 while (rs.next()){ }

1.1  MYSQL中的fetchsize

MYSQL的preparestament基本上不占用内存,为什么呢?因为MYSQL并不需要象oracle那样的一块内存来保存结果集缓冲区,为什么不需要缓冲区,其中根本的原因是由MYSQL的通讯方式决定的。

MYSQL 客户端/服务器协议是半双工的,即MYSQL只能在给定的时间,发送或接受数据,但不能同时发送和接收。所以,MYSQL在数据查询结果集传送的时候,需要一次性将数据全部传送到客户端,在客户数据接收完之后,释放相关的锁等资源。因为这种半双工的通讯方式,所以MYSQL不需要客户端的游标,但是客户端API通过把结果取到内存中,可以模拟游标的操作。所以,我们可以在JAVA程序中,可以象ORACLE那样来实现MYSQL的访问。

 

2. 如何设置fetchsize?

Fetchsize可以在任何一层进行设置 ,ORACLE JDBC驱动默认的FETCHSIZE为10。一般为了方便,我们会在数据源层面上来设置fetchsize。

2.1 语句级别的设置:

我们可以在jdbc中调用Preparedstatement .setFetchSize()的进行设置:

            stmt = conn.prepareStatement(sql);             stmt.setFetchSize(50);

也可以在Ibatis, hibernate等框架上直接针对某个语句进行设置:

< select id="getAllProduct"> select * from employee < /select>

2.2 数据源中的全局设置

JBOSS连接中设置:

< connection-property name="defaultRowPrefetch">50

2.3 Fetchsize的核心源码:

可以在JDBC驱动类Oracle.jdbc.driver.OracleStatment中找到这个方法, setPrefetchInternal方法中传入的默认值为0,伪代码如下:

void setPrefetchInternal(int paramInt){          if (paramInt < 0) {               DatabaseError.throwSqlException(68, "setFetchSize");            }      //获取连接池中的DefaultRowPrefetch属性         else if (paramInt == 0) {               paramInt = this.connection.getDefaultRowPrefetch();          }        if (paramInt == this.defaultRowPrefetch)              return;           this.defaultRowPrefetch = paramInt;         if ((this.currentResultSet == null) || (this.currentResultSet.closed)) {             this.rowPrefetchChanged = true;         } }

3. Fetchsize对性能影响的测试:

3.1 空查询结果集的测试:

查询的表一共有300条记录,测试中查询的结果集为空,执行的是全表扫描。

SQL> select count(*) from test10000;     COUNT(*) ----------        300   SQL> select * from test10000 where col_a='test';   no rows selected
数据库连接方式PSCACHEfetchsize字段长度网络距离总记录数返回记录执行时间 (ms)
ORACLEoci支持11000015KM30001.5875
ORACLEoci支持51000015KM30001.5828
ORACLEoci支持101000015KM30001.7781
ORACLEoci支持501000015KM30002.0468
ORACLEoci支持1001000015KM30002.6656
ORACLEoci支持110000本地30000.1646
ORACLEoci支持510000本地30000.1713
ORACLEoci支持1010000本地30000.1898
ORACLEoci支持5010000本地30000.3431
ORACLEoci支持10010000本地30001.2609
ORACLEthin支持11000015KM30001.6344
ORACLEthin支持101000015KM30001.6687
ORACLEthin支持1001000015KM30001.6266
MYSQLjdbc支持11000015KM30001.5187
MYSQLjdbc支持101000015KM30001.6093
MYSQLjdbc支持1001000015KM30001.5906

 

从上面的测试中,可以得出如下结论:

  1. 在没有记录返回的情况下,OCI方式中fetchsize设置越大,对性能的影响越大。
  2. 在没有记录返回的情况下,THIN和mysql的方式中,fetchsize的大小,对于性能影响不大。

这是在空结果集情况下的影响,仅供参考,不应该作为我们考虑的情况。

3.2 非空查询结果集的测试:

数据库连接方式PSCACHEfetchsize字段长度网络距离总记录数返回记录执行时间 (ms)
ORACLEoci支持110015KM300300226.9533
ORACLEoci支持510015KM30030086.44667
ORACLEoci支持1010015KM30030043.74667
ORACLEoci支持5010015KM30030010
ORACLEoci支持10010015KM3003005.2
ORACLEoci支持110015KM300108.44
ORACLEoci支持510015KM300102.9
ORACLEoci支持1010015KM300101.56
ORACLEoci支持5010015KM300101.56
ORACLEoci支持1100本地30030012.773
ORACLEoci支持5100本地3003005.32
ORACLEoci支持10100本地3003002.9

从上面的测试中,可以得出如下结论:

  1. 当返回结果集较大时,设置较大的fetchsize,对性能会有很大的提升。
  2. Fetchsize设置大于返回的记录数时,对于性能的提升没有任何的意义,反而会增加内存的开销。

3.3 Fetchsize对性能和内存的影响,下面的图可以很好的说明他们的关系:

3.4 Fetchsize和网络的关系

当fetchsize设置到某一值时,便不会再有性能的提升,这不仅仅是因为结果集大小的原因,和操作系统或者ORACLE上的TCP读/写的缓冲区也有关系:

–操作系统上控制网络的读/写 buffer –  net.core.rmem_default = 262144 –  net.core.wmem_default = 262144 –数据库端控制,默认值为操作系统上的设置 : –  RECV_BUF_SIZE=9375000 -  SEND_BUF_SIZE=9375000

4. 总结:

fetchsize的设置,跟具体业务系统有关系,没有一个最好的值可以供各个应用都可以使用。一般OLTP的系统,fetchsize使用jdbc的默认值就可以了。我查看了下网络上的大部分文章,在某个特定的条件下测试的fetchsize,得出一个值,然后所有人都用这个值来设置自己的应用系统。一般情况下,这仅仅只是一些资源的浪费,但是,在某些情况下,如数据源拆分,读写分离架构中,当fetchsize设置的太大,有可能会导致性能的急剧下降,甚至会导致应用上可怕的JVM内存溢出,在不少公司发生过这种惨痛的教训。建议在设置这个值之前,先做一个JVM内存的DUMP,以便能够对内存的占用情况有一个清晰的了解。

摘自:http://www.dbafree.net/?p=597

  评论这张
 
阅读(1228)| 评论(2)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2016