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 example
<?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 example
<?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 example
<?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.