背景

   昨天有同事反映,原先用jboss jndi数据源,现在换成基于spring容器的dbcp配置后,发现原先的请求从5ms,增加到7ms,性能下降了50%。
 
分析
   在服务器上观察了一下请求的profile信息,发现一个请求90%多的时间都在mysql处理: 25%为prepared statement,70%为mysql read数据等待。
 
使用jvisualvm得到的一个time profile的结果:
 
 
发现很明显,preparestatement占了比较大的比例。
 
网上搜索了一把,找到一些相关内容,具体描述: (具体文档可查看附件)
 

    
 
   大致意思也就是说:一个数据库查询,主要可分为两个阶段,一个是prepared statement,另一个是真正的数据库execute。
 
   估算一下:
       statement cost :  7ms * 25% =  1.75ms , 基本符合请求响应时间的现象。
 
解决
   看一下,文档的基本描述: 
   

 

    dbcp的官方文档说明: http://commons.apache.org/dbcp/configuration.html
 
    有两个参数:
 
  • poolPreparedStatements : 表明是否开启statement cache,默认为false,也就是不开启
  • maxOpenPreparedStatements : statement cache的大小,默认为-1,也就是不限制
 
    因为以前使用的是jboss oracle-ds.xml配置,是有配置对应的cache。 
 
Java代码 
  1. <prepared-statement-cache-size>10</prepared-statement-cache-size>  
最后的配置: 
 
Java代码 
  1. <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">  
  2. ....   
  3.     <property name="poolPreparedStatements" value="true" />  
  4.     <property name="maxOpenPreparedStatements" value="10" />  
  5. ....  
  6. </bean>  
 
几点说明:
 
  1. prepared statement cache是针对整个数据库连接池,是整个pool级别
  2. statement cache使用了common pools中的GenericKeyedObjectPool, 利用sql做为key。
  3. 单个connnection独享一个statement cache,也就是说maxOpenPreparedStatements是针对单个connection链接的,这里大家会有存在误用,须注意。 
  4. GenericKeyedObjectPool使用的是Map<Object , List> , 单个key可以对应一个list资源列表,也就是说一个sql key可以有一组list statement。
最后加上了statement cache的profile 结果:


 
已经没有preparestatment的, 总体的响应时间也提升了25%在左右。
 
最后:
 
附上一下c3p0针对statement cache的配置描述:http://www.mchange.com/projects/c3p0/index.html#configuring_statement_pooling
简单的注意下,需要正确理解配置项的意义,不然会出现误用。