Skip to content
221 changes: 153 additions & 68 deletions features/persistent-connections.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
<chapter xml:id="features.persistent-connections" xmlns="http://docbook.org/ns/docbook">
<title>Persistent Database Connections</title>

<simplesect>
<title>What are Persistent Connections?</title>
<simpara>
Persistent connections are links that do not close when the
execution of your script ends. When a persistent connection is
Expand All @@ -13,23 +15,30 @@
the same username and the same password (where applicable).
</simpara>
<simpara>
People who aren't thoroughly familiar with the way web servers work
and distribute the load may mistake persistent connects for what
they're not. In particular, they do <emphasis>not</emphasis> give
you an ability to open 'user sessions' on the same link, they
do <emphasis>not</emphasis> give you an ability to build up a
transaction efficiently, and they don't do a whole lot of other
things. In fact, to be extremely clear about the subject,
persistent connections don't give you <emphasis>any</emphasis>
functionality that wasn't possible with their non-persistent
brothers.
There's no method of requesting a specific connection, or guaranteeing
whether you get an existing connection or a brand new one (if all existing
connections are in use, or the request is being served by a different worker,
which has a separate pool of connections).
</simpara>
<simpara>
Why?
</simpara>
This means that you cannot use PHP's persistent connections to, for example:
</simpara>
<simplelist>
<member>assign a specific database session to a specific web user</member>
<member>create a large transaction across multiple requests</member>
<member>initiate a query on one request and collect the results on another</member>
</simplelist>
<simpara>
This has to do with the way web servers work. There are three ways
in which your web server can utilize PHP to generate web pages.
Persistent connections do not give you <emphasis>any</emphasis>
functionality that wasn't possible with non-persistent connections.
</simpara>
</simplesect>

<simplesect xml:id="persistent-connections.web">
<title>Web Requests</title>
<simpara>
There are two ways in which your web server can utilize PHP to generate
web pages:
</simpara>
<simpara>
The first method is to use PHP as a CGI "wrapper". When run this
Expand All @@ -38,37 +47,52 @@
Because it is destroyed after every request, any resources that it
acquires (such as a link to an SQL database server) are closed when
it is destroyed. In this case, you do not gain anything from trying
to use persistent connections -- they simply don't persist.
to use persistent connections - they simply don't persist.
</simpara>
<simpara>
The second, and most popular, method is to run PHP as a module in a
multiprocess web server, which currently only includes Apache. A
multiprocess server typically has one process (the parent) which
The second, and most popular, method is to run PHP-FPM, or PHP as a module
in a multiprocess web server, which currently only includes Apache.
These setups typically have one process (the parent) which
coordinates a set of processes (its children) who actually do the
work of serving up web pages. When a request comes in from a
client, it is handed off to one of the children that is not already
serving another client. This means that when the same client makes
a second request to the server, it may be served by a different
child process than the first time. When opening a persistent connection,
every following page requesting SQL services can reuse the same
child process than the first time. When opening a persistent connection,
every following page requesting SQL services can reuse the same
established connection to the SQL server.
</simpara>
<note>
<para>
You can check which method your web requests use by checking the value of
"Server API" in the output of <function>phpinfo</function> or the value of
<constant>PHP_SAPI</constant>, run from a web request.
</para>
<para>
If the Server API is "Apache 2 Handler" or "FPM/FastCGI", then persistent
connections will be used across requests served by the same worker. For any
other value, persistent connections will not persist after each request.
</para>
</note>
</simplesect>

<simplesect xml:id="persistent-connections.cli">
<title>Command-line Processes</title>
<simpara>
The last method is to use PHP as a plug-in for a multithreaded web
server. Currently PHP has support for WSAPI, and NSAPI (on
Windows), which all allow PHP to be used as a plug-in on multithreaded
servers like Netscape FastTrack (iPlanet), Microsoft's Internet Information
Server (IIS), and O'Reilly's WebSite Pro. The behavior is essentially
the same as for the multiprocess model described before.
</simpara>
<simpara>
If persistent connections don't have any added functionality, what
are they good for?
As command-line PHP uses a new process for each script, persistent
connections are not shared between command-line scripts, so there is no
value in using them in transient scripts such as crons or commands.
However, they may be useful if, for example, you're writing a long-running
application server that serves many requests or tasks and each may need
their own database connection.
</simpara>
</simplesect>

<simplesect xml:id="persistent-connections.why">
<title>Why Use Them?</title>
<simpara>
The answer here is extremely simple -- efficiency. Persistent
connections are good if the overhead to create a link to your SQL
server is high. Whether or not this overhead is really high depends
Persistent connections are good if the overhead to create a link to your
SQL server is high. Whether or not this overhead is really high depends
on many factors. Like, what kind of database it is, whether or not
it sits on the same computer on which your web server sits, how
loaded the machine the SQL server sits on is and so forth. The
Expand All @@ -83,6 +107,10 @@
you'd have 20 different connections to the SQL server, one from
each child.
</simpara>
</simplesect>

<simplesect xml:id="persistent-connections.drawbacks.conn-limits">
<title>Potential Drawbacks: Connection Limits</title>
<simpara>
Note, however, that this can have some drawbacks if you are using a
database with connection limits that are exceeded by persistent
Expand All @@ -91,41 +119,98 @@
threads attempt to connect, one will not be able to. If there are
bugs in your scripts which do not allow the connections to shut
down (such as infinite loops), the database with only 16 connections
may be rapidly swamped. Check your database documentation for
information on handling abandoned or idle connections.
</simpara>
<warning>
<simpara>
There are a couple of additional caveats to keep in mind when
using persistent connections. One is that when using table
locking on a persistent connection, if the script for whatever
reason cannot release the lock, then subsequent scripts using the
same connection will block indefinitely and may require that you
either restart the httpd server or the database server. Another is
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. In either case, you can
use <function>register_shutdown_function</function> to register a
simple cleanup function to unlock your tables or roll back your
transactions. Better yet, avoid the problem entirely by not using
persistent connections in scripts which use table locks or
transactions (you can still use them elsewhere).
</simpara>
</warning>
<simpara>
An important summary. Persistent connections were designed to have

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would not simply remove this paragraph, especially since you're adding a "drawbacks" section.
the biggest issue i've seen with persistent connections is not people failing to clean up after themselves, but with how they behave more generally (i.e., the "one-to-one" concept mentioned here).

i've seen many beginners, or even experienced programmers that have little exposure to managing sapis/databases/etc., start using persistent connections without sufficient resources (e.g., in a cheap shared hosting environment) or without careful configuration (e.g., assuming you only need one persistent connection because it can be "shared"). in these cases, you're likely to see worse performance. this is actually the only thing i've ever had to explain to a programmer seeking help with persistent connections, and i've had to explain it quite a few times.

Copy link
Contributor Author

@AllenJB AllenJB Oct 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem I had with this paragraph (and this appears to me to be echoed by at least one user comment) is it reads to me as "you can simply enable persistent connections and not have to worry about making any other changes", which obviously isn't true, particularly in cases where PHP is not performing any additional management (eg. PDO MySQL).

Persistent connections can change behavior of scripts when cleanup is not performed.

The situation is not helped by apparent differences in behavior between different extensions. The cleanup performed by mysqli is just one manifestation.

Comparing https://github.com/php/php-src/blob/master/ext/pdo_mysql/tests/pdo_mysql_pconnect.phpt and https://github.com/php/php-src/blob/master/ext/mysqli/tests/mysqli_pconn_reuse.phpt shows PDO MySQL will happily reuse connections even if they are already in use by an existing PDO instance in the same process, while mysqli will not reuse connections until they're "closed". I haven't investigated whether it's required to explicitly close the connection or if this also happens if the mysqli object goes out of scope and is gc'd - I don't know whether there's a difference internally between these 2 actions when dealing with persistent connections.

I did have a quick check to see if there's similar tests that show how PDO PgSQL behaves, but couldn't see any, and I don't have a local postgres server running to perform my own tests. According to https://github.com/php/php-src/blob/master/ext/pgsql/tests/connection_reuse.phpt and https://github.com/php/php-src/blob/master/ext/pgsql/tests/gh13519.phpt both pg_connect and pg_pconnect will give you the same connection instance even if it's in active use by another PgSql\Connection object unless you explicitly request a new connection.

As far as I can see, "persistent connections were designed to be a one to one mapping to regular connections" is not (always) true (depending on the extension / database you're using) in any sense.

It's possible I am misunderstanding what "one-to-one mapping" is supposed to mean in this context, but I appear to not be the only one. Please feel free to submit suggestions for an alternative if you believe it's something not covered by the new version of this page, but I do not believe this paragraph should be restored.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think neither option is good. The previous paragraph was confusing, but the lack of it isn't good either. For now, we can remove it and we can add something better later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made some further amendments across the article that I hope help to clarify the behavior / make the sections on behavior easier to quickly read, and added a "Final Words" section to reiterate the potential for issues and alternative (probably better) solutions.

one-to-one mapping to regular connections. That means that you
should <emphasis>always</emphasis> be able to replace persistent
connections with non-persistent connections, and it won't change
the way your script behaves. It <emphasis>may</emphasis> (and
probably will) change the efficiency of the script, but not its
behavior!
</simpara>
<para>
See also <function>ibase_pconnect</function>, <function>ociplogon</function>,
<function>odbc_pconnect</function>, <function>oci_pconnect</function>,
<function>pfsockopen</function>, and <function>pg_pconnect</function>.
</para>
may be rapidly swamped.
</simpara>
<simpara>
Persistent connections will usually increase the number of connections open
at any given time because idle workers will still hold the connections for
the previous requests they served. If a large number of workers is spun up to
handle an influx of requests, the connections they opened will remain until
the worker is killed or the database server closes the connection.
</simpara>
<simpara>
Ensure that the maximum number of connections allowed by the database server
is greater than the maximum number of web request workers (plus any other
usage such as crons or administrative connections).
</simpara>
<simpara>
Check your database documentation for information on handling abandoned or
idle connections (timeouts). Long timeouts may significantly increase the
number of persistent connections open at any one time.
</simpara>
</simplesect>

<simplesect xml:id="persistent-connections.drawbacks.state">
<title>Potential Drawbacks: Maintaining Connection State</title>
<simpara>
Some database extensions perform automatic cleanup when the connection is
reused; others leave this task at the discretion of the application developer.
Depending on the chosen database extension and the application design, manual
cleanup may be needed before the script exits. Changes that may leave
connections in an unexpected state include:
</simpara>
<simplelist>
<member>Selected / default database</member>
<member>Table locks</member>
<member>Uncommitted transactions</member>
<member>Temporary tables</member>
<member>Connection specific settings or features such as profiling</member>
</simplelist>
<simpara>
Table locks and transactions that are not cleaned up or closed may cause
other queries to be blocked indefinitely and/or cause subsequent reuse of
the connection to cause unexpected changes.
</simpara>
<simpara>
Having the wrong database selected will cause subsequent reuse of the
connection to be unable to execute queries as expected (or execute them on
the wrong database if schemas are similar enough).
</simpara>
<simpara>
If temporary tables are not cleaned up, subsequent requests will not be able
to recreate the same table.
</simpara>
<simpara>
You can implement cleanup using class destructors or
<function>register_shutdown_function</function>. You may also want to
consider dedicated connection pooling proxies that include this as part of
their functionality.
Comment on lines +176 to +177
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd remove this. Without a link or further explanation, we leave the user confused as to what it is and how to use it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's useful to make users aware these tools (with these capabilities) exist.

I don't think there's a necessity to link to specific tools - as I mentioned above I'm both inexperienced with these and want to avoid the manual appearing to recommend specific tools (which may then become unmaintained, or have better alternatives available).

(As someone who hangs out on various forums and chats, there are still have users picking up XAMPP, now 2 years after its last release, and I've been specifically told by at least 1 recently that it was because it was the first tool mentioned in the manual - and still is today!)

Also, the tools are going to be different depending on what database (and/or cloud provider) is used, so this would end up a list of different tools.

I think this provides just enough information to make users aware of these tools and give them the terms to search for them.

</simpara>
</simplesect>

<simplesect xml:id="persistent-connections.final-words">
<title>Final Words</title>
<simpara>
Given their behavior and potential drawbacks described above, you should not
use persistent connections without careful consideration. They should not be
used without implementing additional changes to your application and careful
configuration of your database server and web server and/or PHP-FPM.
</simpara>
<simpara>
Consider alternative solutions such as investigating and fixing the causes of
connection creation overheads (for example, disabling reverse DNS lookups on
the database server), or dedicated connection pooling proxies.
</simpara>
<simpara>
For high volume web APIs, consider using alternative runtimes or long-running
application servers.
</simpara>
</simplesect>

<simplesect role="seealso" xml:id="persistent-connections.seealso">
&reftitle.seealso;
<para>
<simplelist>
<member><function>ibase_pconnect</function></member>
<member><function>oci_pconnect</function></member>
<member><function>odbc_pconnect</function></member>
<member><function>pfsockopen</function></member>
<member><function>pg_connect</function></member>
<member><link linkend="mysqli.persistconns">MySQLi and Persistent Connections</link></member>
<member><link linkend="pdo.connections">PDO Connection Management</link></member>
</simplelist>
</para>
</simplesect>
</chapter>

<!-- Keep this comment at the end of the file
Expand Down
7 changes: 7 additions & 0 deletions reference/pdo/connections.xml
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,13 @@ $dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass, array(
object, the driver will not use persistent connections.
</para>
</note>
<warning>
<para>
PDO does not perform any cleanup of persistent connections. Temporary tables, locks, transactions and other stateful
changes may remain from previous usage of the connection, causing unexpected problems. See the
<link linkend="features.persistent-connections">Persistent Database Connections</link> section for more information.
</para>
</warning>
<note>
<para>
If you're using the PDO ODBC driver and your ODBC libraries support ODBC
Expand Down