Druid连接池(三)
十二、Druid缓存
连接Oracle数据库,打开PSCache,在其他的数据库连接池都会存在内存占用过多的问题,Druid是唯一解决这个问题的连接池。
Oracle数据库下PreparedStatementCache内存问题解决方案:
Oracle支持游标,一个PreparedStatement对应服务器一个游标,如果PreparedStatement被缓存起来重复执行,PreparedStatement没有被关闭,服务器端的游标就不会被关闭,性能提高非常显著。在类似SELECT * FROM T WHERE ID = ?这样的场景,性能可能是一个数量级的提升。
由于PreparedStatementCache性能提升明显,DruidDataSource、DBCP、JBossDataSource、WeblogicDataSource都实现了PreparedStatementCache。
PreparedStatementCache带来的问题
阿里巴巴在使用jboss连接池做PreparedStatementCache时,遇到了full gc频繁的问题。通过mat来分析jmap dump的结果,发现T4CPreparedStatement占内存很多,出问题的几个项目,有的300M,有的500M,最夸张的900M。这些应用都是用jboss连接池访问Oracle数据库,T4CPreparedStatement是Oracle JDBC Driver的PreparedStatement一种实现。 oracle driver不是开源,通过逆向工程以及mat分析,发现其中占内存的是字段char[] defineChars,defineChars大小的计算公式是这样的:
defineChars大小 = rowSize * rowPrefetchCount
rowPrefetchCount在Oracle中,缺省值为10。
其中rowSize是执行查询设计的每一列的大小的和。计算公式是:
rowSize = col_1_size + col_2_size + ... + col_n_size
很悲剧,有些列数据类型是varchar2(4000),于是rowSize巨大,很多个表关联的SQL,rowSize可能高达数十K,再乘以rowPrefetchCount,defineChars大小接近1M。可以想想,maxPoolSize设置为30,PreparedStatementCacheSize设置为50的场景下,是可能导致PreparedStatementCache占据上G的内存。 实际测试得到的结果如下:
varchar2(4000) col_size 4000 chars clob -> col_size col_size 4000 bytes
实际占据内存的公式:
占据内存大小峰值 = defineChars大小 * PreparedStatementCacheSize * MaxPoolSize
我们实际分析,一个应用运行的SQL大约数百条,PreparedStatementCacheSize为50,PreparedStatementCache的算法为LRU,很多的SQL执行之后,在Cache中HitCount为0就被淘汰了,淘汰的过程,其位置从第1移到第50,这个漫长的过程导致了defineChars不能够被young gc回收。
Druid的解决方案
使用OracleDriver提供的PreparedStatementCache支持方法,清理PreparedStatement所持有的buffer。 Oracle在10.x和11.x的Driver中,都提供了如下管理PreparedStatementCache的接口,如下:
package oracle.jdbc.internal; import java.sql.SQLException; public interface OraclePreparedStatement extends oracle.jdbc.OraclePreparedStatement, OracleStatement { public void enterImplicitCache() throws SQLException; public void exitImplicitCacheToActive() throws SQLException; public void exitImplicitCacheToClose() throws SQLException; }
DruidDataSource在管理Oracle PreparedStatement Cache时,调用了上述方法。当调用了enterImplicitCache之后,T4CPreparedStatement中的defineChars和defineBytes都会被清空。
测试表明,通过上述处理,能够有效降低内存。
根据PreparedStatement执行的结果,计算RowPrefetch大小 DrudDataSource对在PreparedStatement.executeQuery和execute方法返回的ResultSet做监控统计执行SQL返回的行数,然后根据统计的结果来设置rowPrefetchSize。例如SQL
SELECT * FROM ORDER WHERE ID = ?
这样的SQL每次返回的纪录数量都是0或者1,根据这个统计的最大值来设置rowPrefetchSize。如果最大值为1,则需要设置rowPrefetchSize为2。
计算公式如下:
int maxRowFetchCount = max(resultSet.size) + 1; if (maxRowFetchCount > defaultRowPrefetch) { maxRowFetchCount = defaultRowPreftech; } prearedStatement.rowPrefetch = maxRowFetchCount;
根据生产环境的监控统计,大多数的SQL返回的行数都是比较小的,通常是1。通过这种算法,能够减少PreparedStatementCache的内存占用。
添加PreparedStatementCache计数器 包括:
PreparedStatementCacheCurrentSize PreparedStatementCacheDeleteCount 缓存删除次数 PreparedStatementCacheHitCount 缓存命中次数 PreparedStatementCacheMissCount 缓存不命中次数 PreparedStatementCacheAccessCount 缓存访问次数
通过这五个计数器,我们清晰了解PreparedStatementCache的工作情况,然后根据实际情况调整。
十三、Druid对比
各种数据库连接池对比
主要功能对比
|
Druid |
BoneCP |
DBCP |
C3P0 |
Proxool |
JBoss |
|
|
LRU |
是 |
否 |
是 |
否 |
是 |
是 |
|
PSCache |
是 |
是 |
是 |
是 |
否 |
否 |
|
PSCache-Oracle-Optimized |
是 |
否 |
否 |
否 |
否 |
否 |
|
ExceptionSorter |
是 |
否 |
否 |
否 |
否 |
是 |
LRU
LRU是一个性能关键指标,特别Oracle,每个Connection对应数据库端的一个进程,如果数据库连接池遵从LRU,有助于数据库服务器优化,这是重要的指标。在测试中,Druid、DBCP、Proxool是遵守LRU的。BoneCP、C3P0则不是。BoneCP在mock环境下性能可能好,但在真实环境中则就不好了。
PSCache
PSCache是数据库连接池的关键指标。在Oracle中,类似SELECT NAME FROM USER WHERE ID = ?这样的SQL,启用PSCache和不启用PSCache的性能可能是相差一个数量级的。Proxool是不支持PSCache的数据库连接池,如果你使用Oracle、SQL Server、DB2、Sybase这样支持游标的数据库,那你就完全不用考虑Proxool。
PSCache-Oracle-Optimized
Oracle 10系列的Driver,如果开启PSCache,会占用大量的内存,必须做特别的处理,启用内部的EnterImplicitCache等方法优化才能够减少内存的占用。这个功能只有DruidDataSource有。如果你使用的是Oracle Jdbc,你应该毫不犹豫采用DruidDataSource。
ExceptionSorter
ExceptionSorter是一个很重要的容错特性,如果一个连接产生了一个不可恢复的错误,必须立刻从连接池中去掉,否则会连续产生大量错误。这个特性,目前只有JBossDataSource和Druid实现。Druid的实现参考自JBossDataSource。
十四、Druid迁移
dbcp迁移:
DruidDataSource的配置是兼容DBCP的。从DBCP迁移到DruidDataSource,只需要修改数据源的实现类就可以了。
DBCP的数据库连接池的实现是:
org.apache.commons.dbcp.BasicDataSource
替换为:
com.alibaba.druid.pool.DruidDataSource
如果需要使用Druid的其他配置,可以参考https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_DruidDataSource%E5%8F%82%E8%80%83%E9%85%8D%E7%BD%AE
例子
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="url" value="${jdbc_url}" />
<property name="username" value="${jdbc_user}" />
<property name="password" value="${jdbc_password}" />
<property name="filters" value="stat" />
<property name="maxActive" value="20" />
<property name="initialSize" value="1" />
<property name="maxWait" value="60000" />
<property name="minIdle" value="1" />
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<property name="minEvictableIdleTimeMillis" value="300000" />
<property name="validationQuery" value="SELECT 'x'" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<property name="poolPreparedStatements" value="true" />
<property name="maxPoolPreparedStatementPerConnectionSize" value="20" />
</bean>
十五、Druid特性
ExceptionSorter是JBoss DataSource中的优秀特性,Druid也有一样功能的ExceptionSorter,但不用手动配置,自动识别生效的。
maxIdle是Druid为了方便DBCP用户迁移而增加的,maxIdle是一个混乱的概念。连接池只应该有maxPoolSize和minPoolSize,druid只保留了maxActive和minIdle,分别相当于maxPoolSize和minPoolSize。
DruidDataSource支持JNDI配置,具体实现的类是这个:
com.alibaba.druid.pool.DruidDataSourceFactory,你可以阅读代码加深理解。
com.alibaba.druid.pool.DruidDataSourceFactory实现了javax.naming.spi.ObjectFactory,可以作为JNDI数据源来配置。
Tomcat JNDI配置
在Tomcat使用JNDI配置DruidDataSource,在/conf/context.xml中,在中加入如下配置:
<Resource name="jdbc/druid-test" factory="com.alibaba.druid.pool.DruidDataSourceFactory" auth="Container" type="javax.sql.DataSource" maxActive="100" maxIdle="30" maxWait="10000" url="jdbc:derby:memory:tomcat-jndi;create=true" />
前半部分是基本信息,不能少的,后半部分是连接池的参数,具体参数看这里,大多数情况driverClassName可以自动识别的
添加Filter
<Resource name="jdbc/druid-test" factory="com.alibaba.druid.pool.DruidDataSourceFactory" auth="Container" type="javax.sql.DataSource" maxActive="100" maxIdle="30" maxWait="10000" url="jdbc:derby:memory:tomcat-jndi;create=true" filters="stat" />
十六、Druid更换
Druid提供了一个中完全平滑迁移DBCP的办法。
1) 从http://repo1.maven.org/maven2/com/alibaba/druid/druid-wrapper/ 下载druid-wrapper-xxx.jar
2) 加入druid-xxx.jar
3) 从你的WEB-INF/lib/中删除dbcp-xxx.jar
4) 按需要加上配置,比如JVM启动参数加上-Ddruid.filters=stat,动态配置druid的filters
这种用法,使得可以在一些非自己开发的应用中使用Druid,例如在sonar中部署druid,sonar是一个使用jruby开发的web应用,写死了DBCP,只能够通过这种方法来更换。
文章转自:http://blog.csdn.net/yinxiangbing/article/details/47905447
Druid连接池(三)的更多相关文章
- Druid连接池
Druid 连接池简介 Druid首先是一个数据库连接池.Druid是目前最好的数据库连接池,在功能.性能.扩展性方面,都超过其他数据库连接池,包括DBCP.C3P0.BoneCP.Proxool.J ...
- spring+mybatis+c3p0数据库连接池或druid连接池使用配置整理
在系统性能优化的时候,或者说在进行代码开发的时候,多数人应该都知道一个很基本的原则,那就是保证功能正常良好的情况下,要尽量减少对数据库的操作. 据我所知,原因大概有这样两个: 一个是,一般情况下系统服 ...
- Mybatis 搭配 阿里druid连接池 连接 oracle 或 mysql
DRUID介绍 DRUID是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0.DBCP.PROXOOL等DB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况,可以说是针 ...
- SpringBoot2.0 基础案例(07):集成Druid连接池,配置监控界面
一.Druid连接池 1.druid简介 Druid连接池是阿里巴巴开源的数据库连接池项目.Druid连接池为监控而生,内置强大的监控功能,监控特性不影响性能.功能强大,能防SQL注入,内置Login ...
- Spring Boot (四): Druid 连接池密码加密与监控
在上一篇文章<Spring Boot (三): ORM 框架 JPA 与连接池 Hikari> 我们介绍了 JPA 与连接池 Hikari 的整合使用,在国内使用比较多的连接池还有一个是阿 ...
- Druid连接池使用
转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/11280540.html 一:DRUID连接池简介 阿里出品的“为监控而生”的数据库连接池,在功能.性能.扩展 ...
- Java开发笔记(一百五十一)Druid连接池的用法
C3P0连接池自诞生以来在Java Web领域反响甚好,业已成为hibenate框架推荐的连接池.谁知人红是非多,C3P0在大型应用场合中暴露了越来越多的局限性,包括但不限于下列几点:1.C3P0管理 ...
- Druid连接池 报错:abandon connection原因分析
问题现象:使用Druid的数据库连接池,在进行一个查询SQL的时候,抛出了异常: [2017-10-20 01:40:59.269 ERROR com.alibaba.druid.pool.Druid ...
- Spring整合JDBC和Druid连接池
我的博客名为黑客之谜,喜欢我的,或者喜欢未来的大神,点一波关注吧!顺便说一下,双十二快到了,祝大家双十二快乐,尽情的买买买~ 如果转载我的文章请标明出处和著名,谢谢配合. 我的博客地址为: https ...
- 一次 Druid 连接池泄露引发的血案!
最近某个应用程序老是卡,需要重启才能解决问题,导致被各种投诉,排查问题是 Druid 连接池泄露引发的血案.. 异常日志如下: ERROR - com.alibaba.druid.pool.GetCo ...
随机推荐
- python的数据存储
Python存储数据 使用json.dump()和json.load() 不管专注的是什么,程序都把用户提供的信息存储在列表和字典等数据结构中.用户关闭程序时,你几乎总是要保存他们提供的信息:一种简单 ...
- Spring Boot 2 实践记录之 MySQL + MyBatis 配置
如果不需要连接池,那么只需要简单的在pom文件中,添加mysql依赖: <dependency> <groupId>mysql</groupId> <arti ...
- ASP.NET开发常用简单实用的方法
ASP.NET开发简单实用的方法 一.打印和导出 打印和导出EXCEL在目前ASP.NET开发中可以说是必要的,有时候针对不同数据难易程度下,用有效快速的方法是解决办法的有效途径之一. 1.打印 后台 ...
- easyui datagrid sort 表头 排序
datagrid的点击列表头刷新,分为两种,一种是页面刷新,不涉及后台服务器数据,不会从新查询数据库,只会刷新当前页数据: 一种是服务器级刷新,会重新加载全部数据. 如果不需要自定义排序,可以直接使用 ...
- DotNetty 使用ByteToMessageDecoder 国家部标808协议封装
DotNetty 开源地址 https://github.com/Azure/DotNetty 个人博客地址 http://www.dncblogs.cn/Blog/ShowBlog/70 1.国 ...
- .Net Core 跨平台应用使用串口、串口通信 ,可能出现的问题、更简洁的实现方法
前些天在学习在 .NET Core下,跨平台使用串口通讯,有一篇文章说到在Linux/物联网下,实现通讯. 主要问题出现在以下两个类库 SerialPortStream flyfire.CustomS ...
- MySQL(外键变种)
day58 外键的变种 a. 用户表和部门表 用户: 不唯一 1 alex ...
- linux中 ll 和ls 区别
ll 列出来的结果详细,有时间,是否可读写等信息 ,象windows里的 详细信息ls 只列出文件名或目录名 就象windows里的 列表 ll -t 是降序, ll -t | tac 是升序 l ...
- Ubuntu 16.04下安装Apache压力测试工具ab
安装 sudo apt-get install apache2-utils 简单使用 # 对http://www.baidu.com/进行100次请求,10个并发请求压力测试结果. ab -n 100 ...
- puppet更新失败
# puppet-updatepuppet: no process foundWarning: Unable to fetch my node definition, but the agent ru ...