本文目录导读:

在 PHP 中,“连接耗尽”通常指的是数据库连接池或外部 API 连接达到最大限制,这里分几种情况说明解决方案:
数据库连接耗尽(最常见)
问题表现
SQLSTATE[HY000] [1040] Too many connectionsPDOException: Connection refused
解决方案
A. 使用连接池(推荐)
// 使用 PDO 连接池(PHP 8.0+)
$pool = new PDO\ConnectionPool(
'mysql:host=localhost;dbname=test',
'username',
'password',
[
'pool_size' => 10, // 最大连接数
'idle_timeout' => 60, // 空闲超时
]
);
$conn = $pool->getConnection();
// 使用完毕后必须归还连接
$pool->release($conn);
B. 减少连接时间
// 设置超时时间
$options = [
PDO::ATTR_TIMEOUT => 5, // 5秒超时
PDO::ATTR_PERSISTENT => true, // 持久连接
];
$pdo = new PDO($dsn, $user, $pass, $options);
C. 使用单例模式
class Database {
private static $instance = null;
private $connection;
private function __construct() {
$this->connection = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
}
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function getConnection() {
return $this->connection;
}
}
Redis/Memcached 连接耗尽
// 使用 Redis 连接池
class RedisPool {
private $connections = [];
const MAX_CONNECTIONS = 10;
public function getConnection() {
// 回收过期连接
foreach ($this->connections as $key => $conn) {
if ($conn->isIdle()) {
return $conn;
}
}
if (count($this->connections) < self::MAX_CONNECTIONS) {
$conn = new Redis();
$conn->connect('127.0.0.1', 6379);
$this->connections[] = $conn;
return $conn;
}
throw new Exception('连接池已满');
}
public function release(Redis $conn) {
// 归还连接
}
}
HTTP/API 连接耗尽(如 cURL)
// 使用 cURL 的多线程处理
$mh = curl_multi_init();
$handles = [];
foreach ($urls as $url) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_multi_add_handle($mh, $ch);
$handles[] = $ch;
}
$running = null;
do {
curl_multi_exec($mh, $running);
curl_multi_select($mh);
} while ($running > 0);
// 关闭所有连接
foreach ($handles as $ch) {
curl_multi_remove_handle($mh, $ch);
curl_close($ch);
}
curl_multi_close($mh);
监控和预防
实时监控连接数量
class ConnectionMonitor {
public static function checkMySQLConnections() {
$pdo = new PDO('mysql:host=localhost', 'user', 'pass');
$stmt = $pdo->query("SHOW STATUS LIKE 'Threads_connected'");
return (int)$stmt->fetchColumn(1);
}
public static function checkRedisConnections() {
$redis = new Redis();
$redis->connect('localhost', 6379);
$info = $redis->info('clients');
return $info['connected_clients'];
}
}
// 设置告警阈值
if (ConnectionMonitor::checkMySQLConnections() > 500) {
// 发送告警邮件/短信
}
最佳实践建议
A. 使用环境变量配置
// .env 文件
DB_CONNECTION_LIMIT=10
DB_TIMEOUT=5
// 代码中使用
$connectionLimit = getenv('DB_CONNECTION_LIMIT');
B. 自动重连机制
class DatabaseConnection {
private $maxRetries = 3;
public function connect() {
for ($i = 0; $i < $this->maxRetries; $i++) {
try {
return $this->createConnection();
} catch (PDOException $e) {
if ($e->getCode() == '1040') { // 连接数超限
usleep(500000); // 等待0.5秒后重试
continue;
}
throw $e;
}
}
throw new Exception('无法获取数据库连接');
}
}
C. 使用优雅降级
try {
$connection = $pool->getConnection();
} catch (Exception $e) {
// 缓存降级策略
$data = getFromCache();
if ($data === null) {
// 从文件系统或其他备用数据源获取
$data = getFromFallback();
}
return $data;
}
服务器配置优化
MySQL 配置示例
# my.cnf max_connections = 1000 wait_timeout = 30 interactive_timeout = 30
PHP-FPM 配置
# php-fpm.conf pm.max_children = 50 pm.start_servers = 10 pm.min_spare_servers = 5 pm.max_spare_servers = 20 pm.max_requests = 500
总结建议
- 实现连接池:对于频繁的连接操作,一定要使用连接池
- 设置合理的超时:避免长时间占用连接
- 监控连接数:及时发现问题
- 使用持久连接:减少创建连接的开销(
ATTR_PERSISTENT) - 合理配置服务器:针对性调整 PHP-FPM 和数据库参数
- 使用队列:对于高并发场景,使用 Redis 队列排队处理
如果问题持续出现,建议检查:
- PHP 版本和框架的连接池支持
- 是否有连接泄漏(未正确关闭连接)
- 服务器内存和文件描述符限制