PHP 8.4.2 Released!

Conexiones persistentes a bases de datos

Las conexiones persistentes son enlaces que no se cierran al finalizar la ejecución de un script. Cuando se solicita una conexión persistente, PHP comprueba si ya hay una idéntica (que ya estuviera abierta antes), utilizándola si existe. Si no, crea el enlace. Una conexión «idéntica» es una conexión que fue abierta por el mismo host, con el mismo usuario y la misma contraseña (donde sea aplicable).

Aquellos que no están plenamente familiarizados con la forma en que trabajan y distribuyen la carga los servidores web podrían confundir para qué sirven las conexiones persistentes. En particular, con ellas no se pueden abrir «sesiones de usuario» en un mismo enlace, no se puede construir una transacción eficiente y no hacen muchísimas otras cosas. De hecho, para ser sumamente precisos, las conexiones persistentes no proporcionan ninguna otra funcionalidad que no fuera posible realizar con sus hermanas no persistentes.

¿Por qué?

Esto tiene que ver con la manera en que los servidores web funcionan. Existen tres formas en las cuales un servidor web puede generar páginas web usando PHP.

El primer método es emplear PHP como una «envoltura» CGI. Cuando se ejecuta de esta forma, se crea y se destruye una instancia del intérprete de PHP por cada solicitud de página (para una página de PHP) al servidor web. Debido a que esta instancia se destruye después de cada solicitud, cualquier recurso que adquiera (tal como un enlace a un servidor de base de datos SQL) es cerrado en la destrucción de dicha instancia. En este caso, no se gana nada utilizando conexiones persistentes: simplemente no persisten.

El segundo método, y más popular, es ejecutar PHP como módulo en un servidor web multiproceso, lo que actualmente solo incluye a Apache. Un servidor multiproceso normalmente tiene un proceso (el padre) que coordina un grupo de procesos (sus hijos) los cuales son los que realmente hacen el trabajo de servir páginas web. Cuando una solicitud proviene de un cliente, esta es cedida a uno de los hijos que no esté ya sirviendo a otro cliente. Esto significa que cuando el mismo cliente hace una segunda solicitud al servidor, esta podría ser servida por un proceso hijo diferente a la primera vez. Cuando se abre una conexión persistente, cada página que solicite servicios SQL puede reusar la misma conexión establecida al servidor SQL.

El último método es utilizar PHP como complemento para un servidor web multihilo. Actualmente, PHP tiene soporte para ISAPI, WSAPI, y NSAPI (en Windows), las cuales permiten usar PHP como un complemento en servidores multihilo como Nestcape FastTrack (iPlanet), Microsoft Internet Information Server (IIS), y O'Reilly's WebSite Pro. El comportamiento es esencialmente el mismo para el modelo multiproceso descrito antes.

Si las conexiones persistentes no tienen ninguna funcionalidad adicional, ¿para que son útiles?

La respuesta es extremadamente simple: Eficacia. Las conexiones persistentes son buenas si la sobrecarga para crear enlaces al servidor SQL es alta. Que esta sobrecarga sea realmente alta o no depende de muchos factores, como el tipo de base de datos que se emplea, si esta se encuentra en la misma computadora en la que está el servidor web, la carga de la máquina donde está el servidor SQL, etc. En resumidas cuentas, si la sobrecarga de una conexión es alta, las conexiones persistentes ayudan considerablemente, haciendo que un proceso hijo únicamente se conecte una vez durante su vida útil, en lugar de hacerlo cada vez que procese una página que requiera una conexión al servidor SQL. Esto significa que cada hijo que abra una conexión persistente tendrá su propia conexión persistente abierta al servidor. Por ejemplo, si se tienen 20 procesos hijos diferentes que ejecutan un script que realiza una conexión persistente al servidor SQL, se tendrán 20 conexiones diferentes al servidor SQL, una por cada hijo.

Observe, sin embargo, que esto puede tener algunos inconvenientes si se está usando una base de datos con un limite de conexiones que sea excedido por las conexiones persistentes hijas. Si la base de datos tiene un limite de 16 conexiones simultáneas, y en el curso de una sesión de un servidor ocupado 17 hilos hijos intentan conectarse, uno de ellos no será capaz de hacerlo. Si un script contiene errores que impidan el cierre de las conexiones (como un bucle infinito), la mencionada base de datos con solamente 16 conexiones podría saturarse rápidamente. Compruebe la documentación de su base de datos para obtener información sobre el manejo conexiones abandonadas o inactivas.

Advertencia

Hay un par de advertencias más a tener en cuenta cuando se utilizan conexiones persistentes. Una es que cuando se usan bloqueos de tablas con una conexión persistente, si el script por alguna razón no puede liberar el bloqueo, los scripts subsiguientes que empleen la misma conexión quedarán en espera indefinidamente, puediendo ser necesario reiniciar el servidor httpd o el servidor de la base de datos. Otra cosa es que cuando se usan transacciones, un bloque de las mismas también acarrearía al siguiente script que utiliza la conexión si la ejecución del script termina antes de que el bloque lo haga. En cualquier caso, se puede usar register_shutdown_function() para registrar una función de limpieza para desbloquear las tablas o deshacer las transacciones. Mejor aún, evitar este problema por completo no usando conexiones persistentes en scripts que utilicen bloqueos de tablas o transacciones (aún se pueden usar en otros lugares).

Un resumen importante. Las conexiones persistentes fueron diseñadas para tener una correspondencia uno a uno con las conexiones normales. Esto significa que siempre se pueden reemplazar las conexiones persistentes por conexiones no persistentes, no cambiando así la forma de funcionar un script. Esto podría (y probablemente lo hará) cambiar la eficacia del script, aunque no su funcionamiento.

Véanse también ibase_pconnect(), ociplogon(), odbc_pconnect(), oci_pconnect(), pfsockopen() y pg_pconnect().

add a note

User Contributed Notes 10 notes

up
17
Tom
14 years ago
There's a third case for PHP: run on a fastCGI interface. In this case, PHP processes are NOT destroyed after each request, and so persistent connections do persist. Set PHP_FCGI_CHILDREN << mysql's max_connections and you'll be fine.
up
14
php at alfadog dot net
10 years ago
One additional not regarding odbc_pconnect and possibly other variations of pconnect:

If the connection encounters an error (bad SQL, incorrect request, etc), that error will return with be present in odbc_errormsg for every subsequent action on that connection, even if subsequent actions don't cause another error.

For example:

A script connects with odbc_pconnect.
The connection is created on it's first use.
The script calls a query "Select * FROM Table1".
Table1 doesn't exist and odbc_errormsg contains that error.

Later(days, perhaps), a different script is called using the same parameters to odbc_pconnect.
The connection already exists, to it is reused.
The script calls a query "Select * FROM Table0".
The query runs fine, but odbc_errormsg still returns the error about Table1 not existing.

I'm not seeing a way to clear that error using odbc_ functions, so keep your eyes open for this gotcha or use odbc_connect instead.
up
12
ynzhang from lakeheadu canada
15 years ago
It seems that using pg_pconnect() will not persist the temporary views/tables. So if you are trying to create temporary views/tables with the query results and then access them with the next script of the same session, you are out of luck. Those temporary view/tables are gone after each PHP script ended. One way to get around this problem is to create real view/table with session ID as part of the name and record the name&creation time in a common table. Have a garbage collection script to drop the view/table who's session is expired.
up
11
pacerier at gmail dot com
9 years ago
Did anyone else notice that the last paragraph contradicts everything above it?

( cached page: https://archive.is/ZAOwy )
up
11
ambrish at php dot net
14 years ago
In IBM_DB2 extension v1.9.0 or later performs a transaction rollback on persistent connections at the end of a request, thus ending the transaction. This prevents the transaction block from carrying over to the next request which uses that connection if script execution ends before the transaction block does.
up
11
christopher dot jones at oracle dot com
17 years ago
For the oci8 extension it is not true that " [...] when using transactions, a transaction block will also carry over to the next script which uses that connection if script execution ends before the transaction block does.". The oci8 extension does a rollback at the end scripts using persistent connections, thus ending the transaction. The rollback also releases locks. However any ALTER SESSION command (e.g. changing the date format) on a persistent connection will be retained over to the next script.
up
8
andy at paradigm-reborn dot com
17 years ago
To those using MySQL and finding a lot of leftover sleeping processes, take a look at MySQL's wait_timeout directive. By default it is set to 8 hours, but almost any decent production server will have been lowered to the 60 second range. Even on my testing server, I was having problems with too many connections from leftover persistent connections.
up
4
jean_christian at myrealbox dot com
22 years ago
If anyone ever wonders why the number of idle db process (open connections) seems to grow even though you are using persistent connections, here's why:

"You are probably using a multi-process web server such as Apache. Since
database connections cannot be shared among different processes a new
one is created if the request happen to come to a different web server
child process."
up
0
fabio
18 years ago
You can in fact provide a port for the connection, take a look at http://de2.php.net/manual/en/function.mysql-pconnect.php#AEN101879

Just use "hostname:port" for the server address.
up
-1
RQuadling at GMail dot com
18 years ago
If you have multiple databases on the same server AND you are using persistent connections, you MUST prefix all the table names with the specific database name.

Changing the database using the xxx_select_db functions alters the database for the connection for all users who are sharing that connection (assuming PHP is running shared and not CGI/CLI).

If you have 2 databases (live and archive) and your script is talking to both, you cannot use 2 persistent connections and change the database for each one.

Internally, persistent connections are used even if you do not specify that you want to use persistent connections. This is why new_link was added to mysql_connect/mssql_connect (PHPV4.2.0+).
To Top