Persistent Connections
A normal PDO connection is opened for a request and released when the request finishes. A persistent connection asks PHP to reuse an existing connection for later requests handled by the same worker process.
Persistent connections can reduce connection overhead, but they are not a free performance switch. They affect database connection counts, transaction state, session settings, temporary tables, locks, and operational behaviour.
Enabling Persistent Connections
PDO uses PDO::ATTR_PERSISTENT to request a persistent connection.
<?php
declare(strict_types=1);
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_PERSISTENT => true,
];
print_r($options);
// Prints:
// Array
// (
// [3] => 2
// [19] => 2
// [12] => 1
// )
The numeric keys are PDO's option constants (PDO::ATTR_ERRMODE, PDO::ATTR_DEFAULT_FETCH_MODE, PDO::ATTR_PERSISTENT, in this case 3, 19, and 12) — print_r() on an options array like this shows the constants' integer values, not their names. In real code, keep the constant name in the source rather than the raw number.
Why They Can Help
Opening database connections has a cost. In some deployments, reusing connections can reduce latency and load on the database server.
Persistent connections are most likely to help when:
- Connection setup is expensive.
- PHP workers handle many requests over time.
- The database connection limit is understood.
- The application resets state between requests.
- Production metrics show connection overhead is a real problem.
Do not enable them just because an application feels slow. Measure first.
Why They Can Hurt
A reused connection may carry state from earlier work if the application or driver does not reset it properly.
Risks include:
- Too many long-lived database connections.
- Open transactions or locks if code fails badly.
- Changed session variables.
- Temporary tables or prepared state behaving unexpectedly.
- Harder debugging because the connection outlives one request.
Many PHP applications perform well with normal non-persistent connections and a properly configured database.
Connection Count
Persistent connections interact with PHP worker counts. If a server has 20 PHP-FPM workers and each keeps a database connection open, that server can hold 20 database connections even while idle.
<?php
declare(strict_types=1);
function possibleConnections(int $servers, int $workersPerServer): int
{
return $servers * $workersPerServer;
}
echo possibleConnections(4, 20) . PHP_EOL;
// Prints:
// 80
Compare that number with the database's maximum connections and with other services that also connect.
Safer Defaults
For beginner and junior PHP work, use non-persistent connections unless a project has a measured reason and an operational plan.
If persistent connections are used, check:
- Transactions always commit or roll back.
- Request-specific database session settings are reset.
- Connection limits are sized for the number of PHP workers.
- Monitoring shows active and idle connections.
- Deploy and restart behaviour is understood.
What To Check
Before moving on, make sure you can:
- Explain what a persistent connection is.
- Enable it with
PDO::ATTR_PERSISTENT. - Explain why it can reduce connection overhead.
- Explain why it can create operational risk.
- Estimate connection counts from server and worker counts.
- Treat persistent connections as a measured decision, not a default.
Practice
Practice: Review Persistent Connection Risk
Write a small PHP helper that estimates persistent connection count and returns a recommendation.
Requirements
- Accept server count, PHP worker count per server, and database max connections.
- Calculate possible persistent connections.
- Warn when possible connections use too much of the database limit.
- Return a short recommendation.
- Include examples for a low-risk and high-risk configuration.
Show solution
This helper does not decide the whole architecture, but it makes the connection-count risk visible.
<?php
declare(strict_types=1);
function persistentConnectionReview(int $servers, int $workersPerServer, int $databaseMaxConnections): array
{
$possibleConnections = $servers * $workersPerServer;
$percentage = $databaseMaxConnections === 0
? 100.0
: ($possibleConnections / $databaseMaxConnections) * 100;
if ($percentage >= 70.0) {
$recommendation = 'Do not enable persistent connections without reducing workers or raising the database limit.';
} else {
$recommendation = 'Connection count looks plausible, but enable only if metrics show connection setup is a bottleneck.';
}
return [
'possible_connections' => $possibleConnections,
'database_limit_used_percent' => round($percentage, 1),
'recommendation' => $recommendation,
];
}
$examples = [
persistentConnectionReview(2, 10, 200),
persistentConnectionReview(6, 30, 200),
];
foreach ($examples as $review) {
echo json_encode($review, JSON_THROW_ON_ERROR) . PHP_EOL;
}
// Prints:
// {"possible_connections":20,"database_limit_used_percent":10,"recommendation":"Connection count looks plausible, but enable only if metrics show connection setup is a bottleneck."}
// {"possible_connections":180,"database_limit_used_percent":90,"recommendation":"Do not enable persistent connections without reducing workers or raising the database limit."}
A real review would also check transaction cleanup, monitoring, database-side idle timeout settings, and whether a connection pooler is a better fit.