databases storage and caching
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:
// [12] => 1
The numeric key is the PDO constant for persistent connections. In real code, keep the constant name.
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.