J'étais à la recherche de certains éléments du pool de connexions JDBC Oracle et je suis tombé sur une nouvelle implémentation (plus récente) du pool Oracle appelée Universal Connection Pool (UCP). Maintenant, cela utilise une nouvelle classe, PoolDataSource, pour la mise en pool des connexions plutôt que OracleDataSource [avec l'option de cache activée]. Je suis en train de débattre de l'opportunité de passer à cette nouvelle implémentation, mais je ne trouve aucune bonne documentation sur les correctifs/mises à niveau (le cas échéant) que cela m'achèterait. Quelqu'un a une expérience avec les deux? Avantages/inconvénients? Merci.
Le dernier pilote Oracle jdbc (11.2.0.1.0) indique explicitement que le cache Oracle Implicit Connection (qui utilise OracleDataSource) est obsolète:
Readme.txt de production des pilotes JDBC Oracle version 11.2.0.1.0
Quoi de neuf dans cette version?
Pool de connexion universel Dans cette version, la fonctionnalité de cache de connexion implicite Oracle est obsolète. Les utilisateurs sont vivement encouragés à utiliser le nouvel Universel Connection Pool à la place. L'UCP possède toutes les fonctionnalités du ICC, et bien plus encore. L'UCP est disponible dans un fichier JAR séparé, ucp.jar.
Donc, je pense qu'il est préférable de commencer à utiliser UCP, mais la documentation n'est pas très bonne ..__ Par exemple, je n'ai pas trouvé le moyen d'utiliser UCP avec Spring ...
UPDATE: J'ai trouvé la configuration de ressort correcte: OK Je pense avoir trouvé la bonne configuration:
<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>
La clé est de spécifier la bonne classe d’usine et la bonne méthode d’usine
PDS est «universel» dans la mesure où il offre le même niveau de fonctionnalité de regroupement que dans ODS pour les bases de données non Oracle, par exemple. MySQL.
Voir Guide de développement UCP , un article sur le site Web d'Oracle et Guide de transition UCP
Je ne vois aucun avantage immédiat à passer de ODS à UCP (PDS), mais peut-être qu’à l’avenir Oracle désapprouvera certaines des fonctionnalités d’ODS. J'ai utilisé ODS pendant un certain temps et j'en suis assez content pour le moment, mais si je recommençais, j'utiliserais PDS.
J'ai effectué une évaluation approfondie de l'UCP et décidé de NE PAS utiliser UCP - veuillez consulter ce post pour plus de détails.
J'ai testé l'UCP et l'ai déployé en production dans une application Spring 3.0.5 Hibernate à l'aide de conteneurs d'écoute Spring JMS et de sessions et transactions gérées par Spring à l'aide de l'annotation @Transactional. Les données entraînent parfois des erreurs de contrainte SQL en raison de threads d'écoute distincts essayant de mettre à jour le même enregistrement. Lorsque cela se produit, l'exception est levée par une méthode annotée par @Transactional et l'erreur est enregistrée dans la base de données à l'aide d'une autre méthode annotée par @Transactional. Pour une raison quelconque, ce processus semble entraîner une fuite du curseur, qui s'additionne finalement et déclenche l'erreur dépassée de limite du curseur ouvert ORA-01000, ce qui provoque l'arrêt du traitement par le thread.
OracleDataSource qui s'exécute dans le même code ne semble pas laisser fuir les curseurs et ne cause donc pas ce problème.
C'est un scénario assez étrange, mais cela m'indique qu'il est un peu tôt pour utiliser l'UCP dans une application avec ce type de structure.
Moi aussi, je teste UCP et je constate des problèmes de performances dans une application basée sur un pool de threads. Au départ, j'ai essayé OracleDataSource, mais je ne parviens pas à le configurer pour le traitement par lots. Je continue à avoir NullPointerExceptions dans les connexions, ce qui me porte à croire que j'ai une fuite de connexion, mais uniquement avec certaines applications, nous gérons d'autres applications qui ne sont pas orientées vers les processus de traitement par lots pour lesquelles OracleDataSource fonctionne correctement.
Basé sur ce post et quelques autres recherches, j'ai essayé UCP. J'ai constaté qu'avec suffisamment d'ajustements, je pouvais me débarrasser des connexions fermées/NullPointerExceptions sur les erreurs de style de connexions, mais Garbage Collection prenait de la vitesse. Le CPG à long terme se remplit rapidement et ne semble jamais se libérer tant que l'application n'est pas terminée. Cela peut parfois prendre jusqu'à un jour ou plus si la charge est vraiment lourde. Je remarque également que le traitement des données prend plus de temps progressivement. Je compare cela à la classe OracleCacheImpl maintenant dépréciée (que nous utilisons actuellement en production car elle fonctionne toujours), où elle utilisait un tiers de la mémoire du GC que UCP utilise et traite les fichiers beaucoup plus rapidement. Dans toutes les autres applications, UCP semble fonctionner correctement et gère à peu près tout ce que je lui lance, mais l'application Thread Pool est une application majeure et je ne pouvais pas risquer d'exceptions GC en production.
La mise en cache implicite de la connexion fonctionne un peu mieux que UCP si vous utilisez la validation de la connexion. Cela correspond au bogue 16723836, qui doit être corrigé dans 12.1.0.2.
La mise en commun UCP devient de plus en plus coûteuse pour obtenir/renvoyer des connexions À mesure que la charge simultanée augmente. Le test compare la mise en cache implicite de la connexion Oracle , Le pooling de Tomcat et le protocole UCP. Tous les 3 sont configurés pour permettre un maximum de 200 connexions, un minimum de 20 connexions et une taille initiale de 2. Tous les 3 sont configurés pour valider les connexions car elles sont supprimées du pool. Le pool Tomcat utilise l'instruction "select Sysdate from dual" pour la validation.
Ces résultats sont obtenus sur un nœud RedHat 64 bits avec 64 cœurs logiques (32 physiques) et 128 Go de RAM.
À 5 threads simultanés, UCP est le plus lent, mais la gestion totale des connexions. Le temps (d’obtention et de fermeture) est inférieur à 1 ms en moyenne ... Au fur et à mesure que la concurrence augmente, UCP est de plus en plus à la traîne. solutions:
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
Il existe deux manières d’utiliser UCP dans Spring Bean.xml.
Pour db.properties défini par un fichier, chargez-le puis utilisez-en un:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:resources/db.properties</value>
</property>
</bean>
Le premier avec 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>
Deuxième avec 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>
C'est ça :) Voici le lien pour la Documentation détaillée: https://docs.Oracle.com/cd/E11882_01/Java.112/e12265/connect.htm#CHDDCICA
J'ai essayé ucp et la performance est meilleure ... Peut-être que la clé utilise cette
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);
Cela aide à emprunter la connexion et à ne jamais la fermer .. la performance est excellente
Le code de la classe de rappel est
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;
}
}