【发布时间】:2010-11-28 12:17:27
【问题描述】:
我正在研究一些 JDBC Oracle 连接池项目,并遇到了一个新的(er)Oracle 池实现,称为通用连接池 (UCP)。现在,它使用一个新类 PoolDataSource 进行连接池,而不是 OracleDataSource [启用缓存选项]。我正在争论是否切换到这个新的实现,但找不到任何好的文档来说明这会给我带来什么(如果有的话)修复/升级。有谁有这两者的经验?优点/缺点?谢谢。
【问题讨论】:
我正在研究一些 JDBC Oracle 连接池项目,并遇到了一个新的(er)Oracle 池实现,称为通用连接池 (UCP)。现在,它使用一个新类 PoolDataSource 进行连接池,而不是 OracleDataSource [启用缓存选项]。我正在争论是否切换到这个新的实现,但找不到任何好的文档来说明这会给我带来什么(如果有的话)修复/升级。有谁有这两者的经验?优点/缺点?谢谢。
【问题讨论】:
最新的 Oracle jdbc 驱动程序 (11.2.0.1.0) 明确指出 Oracle 隐式连接缓存(即使用 OracleDataSource 的缓存)已被弃用:
Oracle JDBC 驱动程序发布 11.2.0.1.0 生产 Readme.txt
此版本有什么新功能?
通用连接池 在此版本中,Oracle 隐式连接缓存功能是 已弃用。强烈建议用户使用新的 Universal 而是连接池。 UCP 具有 ICC,还有更多。 UCP 在单独的 jar 文件中可用, ucp.jar。
所以我认为最好开始使用 UCP,但它的文档并不是那么好。 例如,我没有找到将 UCP 与 spring 一起使用的方法...
更新:我找到了正确的弹簧配置: 好的,我想我找到了正确的配置:
<bean id="dataSource" class="oracle.ucp.jdbc.PoolDataSourceFactory" factory-method="getPoolDataSource">
<property name="URL" value="jdbc:oracle:thin:@myserver:1521:mysid" />
<property name="user" value="myuser" />
<property name="password" value="mypassword" />
<property name="connectionFactoryClassName" value="oracle.jdbc.pool.OracleDataSource" />
<property name="connectionPoolName" value="ANAG_POOL" />
<property name="minPoolSize" value="5" />
<property name="maxPoolSize" value="10" />
<property name="initialPoolSize" value="5" />
<property name="inactiveConnectionTimeout" value="120" />
<property name="validateConnectionOnBorrow" value="true" />
<property name="maxStatements" value="10" />
</bean>
关键是要指定正确的工厂类和正确的工厂方法
【讨论】:
java.lang.ClassNotFoundException: oracle.jdbc.pooling.Factory,尽管我在类路径中有 jars ucp.jar、commons-dbcp.jar 和 ojdbc6.jar
oracle.ucp.jdbc.PoolDataSourceFactory
PDS 是“通用的”,因为它为非 Oracle 数据库提供了与您在 ODS 中获得的相同级别的池功能,例如MySQL。
见UCP Dev Guide、an article on Oracle website和UCP Transition Guide
我没有看到从 ODS 迁移到 UCP (PDS) 的任何直接好处,但未来 Oracle 可能会弃用 ODS 中的某些功能。我使用 ODS 有一段时间了,暂时对它很满意,但如果我刚开始,我会选择 PDS。
【讨论】:
我对 UCP 进行了广泛的评估,并决定不使用 UCP - please have a look at this post 了解详情。
【讨论】:
我测试了 UCP 并将其部署到 Spring 3.0.5 Hibernate 应用程序中,使用 Spring JMS 侦听器容器和使用 @Transactional 注释的 Spring 管理的会话和事务。由于单独的侦听器线程试图更新同一记录,数据有时会导致 SQL 约束错误。发生这种情况时,由@Transactional 注释的一个方法引发异常,并使用@Transactional 注释的另一种方法将错误记录到数据库中。无论出于何种原因,此过程似乎会导致游标泄漏,最终累加并触发 ORA-01000 open cursor limit exceeded 错误,导致线程停止处理任何内容。
在同一代码中运行的 OracleDataSource 似乎不会泄漏游标,因此不会导致此问题。
这是一个非常奇怪的场景,但它表明在具有这种结构的应用程序中使用 UCP 还为时过早。
【讨论】:
我也在测试 UCP,发现自己在基于线程池的应用程序中遇到了性能问题。最初,我尝试了 OracleDataSource,但在将其配置为批处理时遇到了麻烦。我在连接中不断收到 NullPointerExceptions,这让我相信我有某种连接泄漏,但只有在某些应用程序中,我们管理的其他应用程序不是面向批处理的 OracleDataSource 运行良好。
根据这篇文章和我发现的其他一些研究该主题的文章,我尝试了 UCP。我发现通过足够的调整,我可以摆脱连接样式错误上的关闭连接/NullPointerExceptions,但是垃圾收集受到了打击。长期 GC 会很快填满,并且在应用程序完成运行之前似乎永远不会释放。如果负载真的很重,这有时可能需要一天或更长时间。我还注意到,处理数据也需要更长的时间。我将它与现在已贬值的 OracleCacheImpl 类(我们目前在生产中使用,因为它仍然“正常工作”)进行比较,它使用了 UCP 执行的 GC 内存的三分之一,并且处理文件的速度要快得多。在所有其他应用程序中,UCP 似乎工作得很好,几乎可以处理我扔给它的所有东西,但是线程池应用程序是一个主要应用程序,我不能在生产中冒 GC 异常的风险。
【讨论】:
如果您使用连接验证,隐式连接缓存的性能要比 UCP 好很多。这对应于 bug 16723836,计划在 12.1.0.2 中修复。
UCP 池的获取/返回成本越来越高 随着并发负载的增加而连接。测试对比oracle 隐式连接缓存、tomcat 的池化和 UCP。 3个都是 配置为允许最多 200 个连接,最少 20 个连接和 初始大小为 2。所有 3 个都配置为将连接验证为 它们被从池中移除。 tomcat 池使用语句“select sysdate from dual”进行验证。
这些结果是在具有 64 个逻辑内核(32 个物理内核)和 128 GB 内存的 64 位 RedHat 节点上得出的。
在 5 个并发线程时,UCP 是最慢的,但完全连接管理 时间(获取和关闭)平均不到 1 毫秒。 随着并发的增加,UCP 越来越落后于 其他解决方案:
25 Threads:
Implicit: 0.58ms
Tomcat: 0.92ms
UCP: 1.50ms
50 Threads:
Implicit: 0.92ms
Tomcat: 1.60ms
UCP: 6.80ms
100 Threads:
Implicit: 2.60ms
Tomcat: 3.20ms
UCP: 21.40ms
180 Threads:
Implicit: 13.86ms
Tomcat: 15.34ms
UCP: 40.70ms
【讨论】:
在 Spring Bean.xml 中使用 UCP 有两种可能的方式。
对于由某个文件设置的 db.properties,然后加载它然后使用其中之一:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:resources/db.properties</value>
</property>
</bean>
第一个带有 oracle.ucp.jdbc.PoolDataSourceImpl :-
<bean id="dataSource" class="oracle.ucp.jdbc.PoolDataSourceImpl">
<property name="URL" value="${jdbc.url}" />
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="validateConnectionOnBorrow" value="true"/>
<property name="connectionFactoryClassName" value="oracle.jdbc.pool.OracleDataSource" />
<property name="connectionPoolName" value="TEST_POOL" />
<property name="minPoolSize" value="10" />
<property name="maxPoolSize" value="20" />
<property name="initialPoolSize" value="12" />
</bean>
第二个是 oracle.ucp.jdbc.PoolDataSourceFactory :-
<bean id="dataSource" class="oracle.ucp.jdbc.PoolDataSourceFactory"
factory-method="getPoolDataSource">
<property name="URL" value="${jdbc.url}" />
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="validateConnectionOnBorrow" value="true"/>
<property name="connectionFactoryClassName" value="oracle.jdbc.pool.OracleDataSource" />
<property name="connectionPoolName" value="TEST_POOL" />
<property name="minPoolSize" value="10" />
<property name="maxPoolSize" value="20" />
<property name="initialPoolSize" value="12" />
</bean>
就是这样:) 这是详细文档的链接: https://docs.oracle.com/cd/E11882_01/java.112/e12265/connect.htm#CHDDCICA
【讨论】:
我试过ucp,性能更好……可能是关键在用这个
oracle.ucp.jdbc.PoolDataSource ds = (oracle.ucp.jdbc.PoolDataSource)envContext.lookup(url_r);
MyConnectionLabelingCallback callback = new MyConnectionLabelingCallback();
ds.registerConnectionLabelingCallback( callback );
Properties label = new Properties();
label.setProperty(pname, KEY);
conn = ds.getConnection(label);
这有助于借用连接并且永远不会关闭它..所以性能很棒
回调类的代码是
public class MyConnectionLabelingCallback
implements ConnectionLabelingCallback {
public MyConnectionLabelingCallback()
{
}
public int cost(Properties reqLabels, Properties currentLabels)
{
// Case 1: exact match
if (reqLabels.equals(currentLabels))
{
System.out.println("## Exact match found!! ##");
return 0;
}
// Case 2: some labels match with no unmatched labels
String iso1 = (String) reqLabels.get("TRANSACTION_ISOLATION");
String iso2 = (String) currentLabels.get("TRANSACTION_ISOLATION");
boolean match =
(iso1 != null && iso2 != null && iso1.equalsIgnoreCase(iso2));
Set rKeys = reqLabels.keySet();
Set cKeys = currentLabels.keySet();
if (match && rKeys.containsAll(cKeys))
{
System.out.println("## Partial match found!! ##");
return 10;
}
// No label matches to application's preference.
// Do not choose this connection.
System.out.println("## No match found!! ##");
return Integer.MAX_VALUE;
}
public boolean configure(Properties reqLabels, Object conn)
{
System.out.println("Configure################");
try
{
String isoStr = (String) reqLabels.get("TRANSACTION_ISOLATION");
((Connection)conn).setTransactionIsolation(Integer.valueOf(isoStr));
LabelableConnection lconn = (LabelableConnection) conn;
// Find the unmatched labels on this connection
Properties unmatchedLabels =
lconn.getUnmatchedConnectionLabels(reqLabels);
// Apply each label <key,value> in unmatchedLabels to conn
for (Map.Entry<Object, Object> label : unmatchedLabels.entrySet())
{
String key = (String) label.getKey();
String value = (String) label.getValue();
lconn.applyConnectionLabel(key, value);
}
}
catch (Exception exc)
{
return false;
}
return true;
}
}
【讨论】: