Pools de conexiones a base de datos: Apache DBCP y c3p0

En ocasiones anteriores he hablado de este tema, someramente, pero en esta ocasión quiero ya ahondar un poco más y hablar de un par de implementaciones de este mecanismo.

"Pool" en inglés no solamente significa piscina, sino también se usa para describir un conjunto de elementos disponibles para ser utilizados de alguna forma. Un pool de conexiones a base de datos es un mecanismo para optimizar el desempeño de una aplicación así como la utilización de recursos, teniendo varias conexiones ya establecidas al RDBMS, las cuales pueden ser utilizadas por cualquier proceso que las necesite.

Esto significa que en vez de que un componente establezca su propia conexión, la toma del pool y al final cuando ya no la necesita, la devuelve al pool. Hay incluso maneras transparentes de lograr esto, utilizando una implementación especial de DataSource, la cual cuando se le pide una conexión, tome una del pool, y dicha conexión se devuelva al pool cuando se invoque   (en vez de cerrarse físicamente).

El uso de un pool de conexiones mejora mucho el desempeño de una aplicación que hace uso intensivo de una base de datos, ya que el costo de abrir una nueva conexión a la base de datos consume tiempo y recursos tanto en el cliente como en el servidor (y sobre todo puede ser costoso en el servidor de base de datos). Si se tienen varias conexiones abiertas desde el principio y se reutilizan constantemente, se utiliza menos memoria en el servidor y el tiempo de respuesta mejora en el cliente, puesto que se ahorra el tiempo de establecer una conexión TCP y autentificarla con el protocolo de la base de datos (usuario/password, etc).

El manejo directo de un pool de conexiones es un tanto complicado, por lo que ya se han desarrollado varias bibliotecas de software que resuelven el problema. De hecho los contenedores grandes JEE por lo general incluyen sus propios mecanismos para manejar pools a base de datos; sin embargo, contenedores más simples como Jetty o Tomcat por lo general no incluyen estas facilidades, además de que se puede dar el caso de necesitar algo así en una aplicación de Java Estándar.

En este artículo voy a hablar de dos opciones: Apache DBCP (Database Connection Pool, parte del proyecto Commons) y C3P0. Ambos proyectos son software libre, bajo las licencias APL y LGPL respectivamente.

Ambas opciones resuelven el problema de manera similar: Implementar una subclase especial de DataSource que administra el pool de conexiones, así como un objeto especial que implementa la interfaz java.sql.Connection, pero que simplemente envuelve una conexión real, solamente sobreescribiendo el método   para que se devuelva la conexión al pool en vez de cerrarla físicamente.

En ambos casos, el pool comienza teniendo conexiones disponibles. Cuando un objeto pide una conexión al pool, se marca como activa; cuando se devuelve la conexión al pool se marca como disponible nuevamente. Si no hay conexiones disponibles y un objeto necesita una, hay distintos comportamientos que se pueden configurar: uno es que se genere una nueva conexión, y otro es esperar a que una conexión regrese al pool (se debe establecer un límite de tiempo de espera).

Apache DBCP

Vayamos primero con DBCP. La manera de utilizarlo, es crear una instancia de   a la cual debemos configurarle varios parámetros, dependiendo de las necesidades de nuestra aplicación. Las propiedades más esenciales son driverClassName, url, username y password, que creo que ni siquiera necesitan explicación, simplemente son indispensables para que el DataSource se pueda conectar a una base de datos. Algunas propiedades interesantes son:

maxActive
El límite de conexiones que puede haber activas (es decir fuera del pool).
maxIdle
El límite de conexiones que debe haber disponibles en el pool.
minIdle
El mínimo de conexiones que debe haber disponibles en el pool en todo momento.
initialSize
El número de conexiones que se deben crear cuando se inicializa el pool.
maxWait
El límite de tiempo, en milisegundos, que el pool debe esperar para que regrese una conexión al pool. Este parámetro se utiliza cuando un objeto pide una conexión, ya no hay conexiones disponibles, pero se ha alcanzado el límite de conexiones activas (por lo tanto ya no se deben crear más). En este caso el pool esperará el tiempo establecido en este parámetro a que regrese una conexión y si ninguna se libera al transcurrir este lapso, se arroja una excepción.
validationQuery
Esta propiedad opcional puede especificar un query que se ejecuta en una conexión antes de prestarla a un objeto en la aplicación. De este modo, si el query falla, la conexión se marca como inválida, es desechada y se elimina del pool, creando una nueva si es necesario. Esto es muy útil para cuando se pierden conexiones por periodos prolongados de inactividad, pues permite que se creen nuevas de manera automática.

Hay algunas otras propiedades que nos permiten validar una conexión antes de devolverla al pool (por si el objeto que la utilizó la dejó en un estado inválido), ejecutar un query en periodos de inactividad para mantener vivas las conexiones, marcar si queremos autoCommit o no en las conexiones, marcar el nivel de aislamiento default para las conexiones, etc. Pero por ahora nos concentraremos en lo más básico.

C3P0

C3P0 por su parte nos ofrece la clase  , que es un tanto similar a BasicDataSource de DBCP, excepto que cambian algunos nombres y algunos mecanismos de validación. En esta clase, las propiedades esenciales para la conexión se llaman driverClass, jdbcUrl, user y password. La configuración varía un poco, siendo las siguientes propiedades las principales:

minPoolSize
El número mínimo de conexiones que debe tener el pool (la suma de conexiones activas e inactivas
maxPoolSize
El número máximo de conexiones que debe manejar el pool (la suma de conexiones activas e inactivas; cuando todas las conexiones están activas, solamente se podrán crear nuevas conexiones si este número no ha sido alcanzado).
acquireIncrement
Esta propiedad indica el número de conexiones a crear cuando ya no hay disponibles. Esto significa que si todas las conexiones están activas y todavía no se ha llegado a maxPoolSize, en vez de crear solamente una conexión nueva, se crean tantas como se indique aquí. Esto puede optimizar el desempeño para los objetos que necesiten más conexiones posteriormente.
automaticTestTable
C3P0 valida las conexiones que maneja antes de prestarlas, haciendo un query a una tabla de prueba, la cual debe tener una sola columna y no debe tener registros. En esta propiedad se indica el nombre de dicha tabla, para que C3P0 haga un SELECT a la misma para validar la conexión.
checkoutTimeout
En esta propiedad se define el tiempo máximo de espera para que regrese una conexión al pool, cuando ya no se pueden crear más conexiones y todas están ocupadas, y un objeto pide una conexión.
numHelperThreads
C3P0 realiza ciertas operaciones, como la creación de nuevas conexiones, validaciones, etc, en hilos separados. En apicaciones que hacen uso intensivo del pool se puede incrementar este parámetro si se detecta que el pool está siendo un cuello de botella.

Personalmente he usado ambas opciones y he encontrado que el desempeño es muy similar bajo condiciones normales, sin embargo tal parece que C3P0 maneja mejor algunas condiciones de error como por ejemplo que se pierdan todas las conexiones al servidor de base de datos (porque el servidor de murió, o el cliente se desconectó de la red, etc). Apache DBCP depende de Commons-Pool, una biblioteca de Apache que permite manejar pools de cualquier objeto, mientras que C3P0 no tiene dependencias externas.

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de samz550a

wow, genial tu aporte

No mas he visto el título y la cantidad de texto en que explicas y me anima mucho... hace tiempo estuve buscando información sobre el tema y en español no habia mucha información........... así que me tocó utilizar el código mas o menos sin saber bien que era lo que hacia.

Muchas gracias.

Imagen de luxspes

Tomcat JDBC Pool: Asincronica y Multihilo

Es posible que nuevas versiones de Tomcat ya no usen DBCP (ni C3P0) si no una nueva implementacion de connection pool llamada "Tomcat JDBC Pool "

Aparentement DBCP tiene varias limitaciones que la nueva Tomcat JDBC Pool corrige. La verdad no he tenido oportunidad de probarla, pero aparentemente podira ofrecer mejor desempeño debido que tiene un diseño que aprovecha mejor las capacidades multi-hilo de Java y que obtiene las conexiones asincronicamente.

Alguno de los lectores de este sitio la ha probado? Cuales han sido sus resultados?

Imagen de ezamudio

Tomcat JDBC Pool

No sabía de su existencia... pero lo que dices de obtener conexiones asincrónicamente, suena similar a lo que hace c3p0, tal vez hicieron una implementación nueva tomando las mejores ideas de estas dos, habrá que revisarla. Por cierto, Esta es la liga, la que tú pusiste es al plugin de Grails para usar el pool de Tomcat en vez del default.

Yo hice un benchmark muy simple para comparar c3p0 y DBCP pero parece que no abarca ese aspecto de obtener más conexiones nuevas, solamente obtiene unas pocas y después las reutiliza constantemente; ahí el performance es muy similar. La queja con DBCP es que es lento para conexiones nuevas, pero pues idealmente eso debe ser muy esporádico en un pool...

Lo único que no me gustó ahorita que lo leí es esa dependencia con JULI. Cómo puedes integrar eso con Log4J, JCL, SLF4J, JUL, etc?

Imagen de luxspes

JULI->log4j->slf4j ?

Cómo puedes integrar eso con Log4J, JCL, SLF4J, JUL, etc?

Aparentemente puede hacerse que JULI use log4j como mecanismo de logging... y como SLF4J puede hacerse pasar por log4j...

Imagen de ezamudio

Es un problema...

OK la aplicación con la que puedo probar esto, usa slf4j como API para logging pero log4j como backend. Sería cosa de configurar JULI para usar log4j y no necesito usar lo de log4j-over-slf4j

Imagen de ezamudio

BoneCP

Y me acabo de encontrar otra opción: BoneCP, y aplicando el mismo benchmark que le hice a DBCP y C3P0, es consistentemente más rápido que ambos (el benchmark corre en aprox 70% del tiempo que tomaba a los otros dos).

Habrá que echarle un ojo. Tiene dependencia con Google Collections.

Muy buena información

Voy a tomar en cuenta para cuando tenga que hacer conexiones de BD... :) no sabia mucho de esto... buena información

Imagen de ezamudio

Nueva versión de DBCP

Ayer revisé la página de DBCP y apenas me di cuenta que ya salió la versión 1.4, junto con commons-pool versión 1.5.4; ya hice pruebas y el performance mejoró un poco; la principal diferencia es que ahora trae soporte para JDBC nivel 4, utilizando un TransactionManager y conexiones XA.

Imagen de skuarch

chido !!!

antes el pool de conexiones lo realizaba de dos formas, directamente en el servidor de aplicaciones o con apache dbcp, y gracias a ti ya conosco otra nueva forma de crear pools.

duda

Tengo un problema grave, hay una aplicacion grande el cual en algun lado se paso poner para que libere la conexion de base datos
ahorita marca que no se puede conectar , como puedo hacer para rastrear via algun programa o se puede hacer algo por configuración para que libere las conexiones con mucho tiempo..?alguna sugerencia

ademas tengo esta configuración es la adecuada???

initialSize="10"
maxActive="60"
maxIdle="15"
minIdle="15"
maxWait="5000"
validationQuery="SELECT 1 FROM DUAL"
testOnBorrow="false"
testWhileIdle="true"
timeBetweenEvictionRunsMillis="600000"
numTestsPerEvictionRun="5"
minEvictableIdleTimeMillis="420000"
removeAbandoned="true"
removeAbandonedTimeout="10"
logAbandoned="false"

Agradecimiento

Amigote, gracias, por la concisa explicación, me orientaste bastante sobre dbcp, viva! JavaMexico.