PHP Conference Nagoya 2025

Conexões Persistentes com o Banco de Dados

Conexões persistentes são conexões que não fecham quando a execução do seu script termina. Quando uma conexão persistente é requisitada, o PHP verifica se já existe uma conexão persistente idêntica (que foi mantida aberta anteriormente) - e, se ela existir, ele a usa. Se ela não existir, ele cria a conexão. Uma conexão 'idêntica' é uma conexão que foi aberta para o mesmo host, com o mesmo nome de usuário e a mesma senha (onde for aplicável).

Pessoas que não são totalmente familiarizadas com a maneira como servidores web trabalham e distribuem a carga podem confundir conexões persistentes com o que elas não são. Em particular, elas não dão a você a capacidade de abrir 'sessões de usuários' na mesma conexão, elas não dão a você a capacidade de construir uma transação eficientemente, e elas não fazem muitas outras coisas. De fato, para ser extremamente claro sobre o assunto, conexões persistentes não te dão nenhuma funcionalidade que não era possível com as suas correspondentes não-persistentes.

Por que?

Isso tem a ver com a maneira como os servidores web funcionam. Existem três maneiras em que seu servidor web pode utilizar PHP para gerar páginas.

O primeiro método é usar o PHP como um CGI "wrapper". Quando executado dessa maneira, uma instância do interpretador do PHP é criada e destruída para cada requisição de página (para uma página PHP) para o seu servidor web. Como ela é destruída após cada requisição, quaisquer recursos que ela adquirir (como uma conexão para um servidor de banco de dados SQL) são fechados quando ela é destruída. Nesse caso, você não ganha nada por tentar usar conexões persistentes -- elas simplesmente não persistem.

O segundo método, e mais popular, é rodar o PHP como um módulo em um servidor com multi-processos, que atualmente só inclui o Apache. Um servidor com multi-processos tipicamente tem um processo (o pai) que coordena uma série de processos (seus filhos) que realmente fazem o trabalho de servir as páginas web. Quando uma requisição chega de um cliente, ela é entregue à um dos filhos que já não estiver servindo outro cliente. Isso significa que quando o mesmo cliente faz uma segunda requisição para o servidor, ele pode ser atendido por um processo filho diferente do que da primeira vez. Ao abrir uma conexão persistente, cada página que requisitar serviços de SQL pode reutilizar a mesma conexão estabelecida ao servidor SQL.

O último método é usar o PHP como um plug-in para um servidor web multithread. Atualmente, o PHP tem suporte para ISAPI, WSAPI, e NSAPI (no Windows), todos permitindo PHP ser usado como um plug-in em servidores multithreaded como Netscape FastTrack (iPlanet), Internet Information Server (IIS) da Microsoft, e O'Reilly's WebSite Pro. O comportamento é essencialmente o mesmo do modelo multiprocessado descrito anteriormente.

Se conexões persistentes não tem nenhuma funcionalidade a mais, para que elas servem?

A resposta é extremamente simples: eficiência. Conexões persistentes são boas se a sobrecarga (overhead) para criar uma conexão ao seu servidor SQL for alta. Se essa sobrecarga é alta ou não depende de vários fatores. Como, qual tipo de banco de dados é, se está ou não na mesma máquina onde o servidor web está, o quão carregada a máquina onde está o servidor SQL está e assim por diante. O ponto é que se a sobrecarga de conexão for alta, conexões persistentes ajudam consideravelmente. Elas fazem com que os processos filhos simplesmente se conectem uma vez só durante toda sua duração, ao invés de cada vez que eles processam uma página que requer uma conexão ao servidor SQL. Isso significa que cada filho que abriu uma conexão persistente terá sua própria conexão persistente para o servidor. Por exemplo, se você tivesse 20 processos filhos diferentes que rodassem um script que fizesse uma conexão persistente à um servidor SQL, você teria 20 conexões diferentes servidor SQL, uma para cada filho.

Perceba, no entanto, que isso pode ter algumas desvantagens se você estiver usando um banco de dados com limite de conexões que são excedidas pela conexões persistentes dos filhos. Se o seu banco de dados tem um limite de 16 conexões simultâneas, e durante um momento de pico de acessos, 17 processos filhos tentarem se conectar, um deles não será capaz. Se houver bugs no seus scripts que não permitem que as conexões se fechem (como loops infinitos) o banco de dados com apenas 16 conexões pode rapidamente ficar sobrecarregado. Procure na documentação do seu banco de dados por informações sobre como lidar com conexões ociosas ou abandonadas.

Aviso

Existem mais alguns cuidados a se tomar quando usando conexões persistentes. Um deles é que, quando usando travamento de tabela em uma conexão persistente, se o script por qualquer razão não puder destravar a mesma, então scripts subsequentes usando a mesma conexão serão bloqueados indefinidamente e podem exigir que você ou reinicie o servidor httpd ou o servidor de banco de dados. Outro cuidado, é que quando usando transações, um bloco de transação também será carregado para o próximo script que usa aquela conexão se a execução do script terminar antes que o bloco de transação termine. Em ambos os casos, você pode usar register_shutdown_function() para registrar uma função simples de limpeza para destravar suas tabelas ou reverter suas transações. Melhor ainda, evite o problema completamente não utilizando conexões persistentes em scripts que usam travamento de tabelas ou transações (você ainda pode usar eles em outros casos).

Um resumo importante. Conexões persistente foram feitas para ter um mapeamento de um-para-um com conexões normais. Isso significa que você deve sempre ser capaz de substituir conexões persistentes com conexões não-persistentes e isso não mudará a maneira como seu script se comporta. Isso pode (e provavelmente irá) mudar a eficiência do seu script, mas não o seu comportamento!

Veja também ibase_pconnect(), ociplogon(), odbc_pconnect(), oci_pconnect(), pfsockopen() e pg_pconnect().

adicione uma nota

Notas Enviadas por Usuários (em inglês) 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