本文目录导读:

在PHP项目中解决数据库连接超时问题,通常需要从数据库配置、PHP代码、网络环境和数据库服务器四个层面入手,以下是详细的解决方案:
数据库连接配置优化
MySQL连接超时设置
// PDO连接示例 - 设置超时时间
$options = [
PDO::ATTR_TIMEOUT => 5, // 连接超时(秒)
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
];
$pdo = new PDO('mysql:host=localhost;dbname=test', $user, $pass, $options);
MySQLi连接示例
$mysqli = new mysqli();
$mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, 5); // 设置5秒超时
$mysqli->real_connect('localhost', 'user', 'pass', 'db');
配置文件优化
my.cnf/my.ini 数据库配置
[mysqld] # 连接超时设置 wait_timeout = 28800 # 空闲连接超时时间(秒) interactive_timeout = 28800 # 交互式连接超时 connect_timeout = 10 # 连接超时时间(秒) max_connections = 500 # 最大连接数 # 查询超时设置 max_execution_time = 30000 # 查询执行时间限制(毫秒) lock_wait_timeout = 3600 # 锁等待超时(秒)
PHP代码层面的解决方案
1 连接重试机制
function connectWithRetry($maxRetries = 3, $timeout = 5) {
$attempt = 0;
while ($attempt < $maxRetries) {
try {
$pdo = new PDO(
'mysql:host=localhost;dbname=test',
'user',
'pass',
[PDO::ATTR_TIMEOUT => $timeout]
);
return $pdo;
} catch (PDOException $e) {
$attempt++;
if ($attempt >= $maxRetries) {
throw new Exception("数据库连接失败,已重试 {$maxRetries} 次");
}
sleep(1); // 等待1秒后重试
}
}
}
2 连接池实现
class ConnectionPool {
private static $connections = [];
private static $lastUsed = [];
public static function getConnection() {
$key = md5('mysql:host=localhost;dbname=test');
// 清理过期连接
self::cleanExpiredConnections();
// 检查是否有可用连接
if (isset(self::$connections[$key]) &&
self::isConnectionAlive(self::$connections[$key])) {
self::$lastUsed[$key] = time();
return self::$connections[$key];
}
// 创建新连接
self::$connections[$key] = self::createNewConnection();
self::$lastUsed[$key] = time();
return self::$connections[$key];
}
private static function cleanExpiredConnections() {
foreach (self::$connections as $key => $conn) {
if (time() - self::$lastUsed[$key] > 300) { // 5分钟未使用
$conn = null;
unset(self::$connections[$key]);
unset(self::$lastUsed[$key]);
}
}
}
private static function isConnectionAlive($pdo) {
try {
$pdo->query('SELECT 1');
return true;
} catch (PDOException $e) {
return false;
}
}
private static function createNewConnection() {
return new PDO(
'mysql:host=localhost;dbname=test',
'user',
'pass',
[
PDO::ATTR_TIMEOUT => 5,
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'"
]
);
}
}
// 使用示例
$pdo = ConnectionPool::getConnection();
3 设置PHP执行时间
// 在长时间查询前设置
set_time_limit(300); // 5分钟
// 或使用更优雅的方式
if (function_exists('set_time_limit')) {
set_time_limit(0); // 不限制(不推荐用于Web环境)
}
网络层面的优化
1 持久连接(谨慎使用)
// PDO持久连接
$options = [
PDO::ATTR_PERSISTENT => true, // 启用持久连接
PDO::ATTR_TIMEOUT => 5
];
$pdo = new PDO('mysql:host=localhost;dbname=test', $user, $pass, $options);
// MySQLi持久连接
$mysqli = new mysqli('p:localhost', 'user', 'pass', 'db'); // 前缀 p: 表示持久连接
2 使用数据库代理
// 配置连接代理(如ProxySQL, HAProxy)
$config = [
'host' => 'proxy-server', // 代理服务器地址
'port' => 3306,
'timeout' => 2, // 缩短超时时间
'retry' => 2 // 自动重试
];
监控与预防措施
1 健康检查定时器
class DatabaseHealthCheck {
public static function check($pdo) {
try {
$start = microtime(true);
$pdo->query('SELECT 1');
$elapsed = microtime(true) - $start;
if ($elapsed > 3) { // 响应时间超过3秒预警
error_log("数据库响应缓慢: {$elapsed}秒");
}
return true;
} catch (Exception $e) {
error_log("数据库健康检查失败: " . $e->getMessage());
return false;
}
}
}
// 定期执行健康检查(建议每隔60秒)
register_shutdown_function(function() use ($pdo) {
DatabaseHealthCheck::check($pdo);
});
2 懒加载连接
class Database {
private $connection = null;
public function query($sql) {
if ($this->connection === null) {
$this->connection = $this->createConnection();
}
// 检查连接是否有效
if (!$this->isAlive()) {
$this->connection = $this->createConnection();
}
return $this->connection->query($sql);
}
private function isAlive() {
try {
$this->connection->query('SELECT 1');
return true;
} catch (Exception $e) {
return false;
}
}
}
数据库服务器优化
1 MySQL配置优化
-- 查看当前连接超时设置 SHOW VARIABLES LIKE '%timeout%'; -- 动态修改(临时有效) SET GLOBAL wait_timeout = 28800; SET GLOBAL interactive_timeout = 28800; SET GLOBAL max_connections = 500;
2 索引优化
-- 检查慢查询 EXPLAIN SELECT * FROM users WHERE email = 'test@example.com'; -- 创建索引 CREATE INDEX idx_email ON users(email); CREATE INDEX idx_status_created ON orders(status, created_at);
最佳实践建议
1 错误处理模板
class DatabaseConnection {
private static $instance = null;
private $connection = null;
private $config = [
'host' => 'localhost',
'dbname' => 'test',
'user' => 'root',
'pass' => 'password',
'timeout' => 3,
'maxRetries' => 3,
'retryDelay' => 2
];
public function connect() {
$attempts = 0;
$lastError = null;
while ($attempts < $this->config['maxRetries']) {
try {
$start = microtime(true);
$this->connection = new PDO(
"mysql:host={$this->config['host']};dbname={$this->config['dbname']}",
$this->config['user'],
$this->config['pass'],
[
PDO::ATTR_TIMEOUT => $this->config['timeout'],
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4"
]
);
$elapsed = microtime(true) - $start;
if ($elapsed > $this->config['timeout']) {
error_log("连接耗时较长: {$elapsed}秒");
}
return $this->connection;
} catch (PDOException $e) {
$lastError = $e;
$attempts++;
if ($attempts < $this->config['maxRetries']) {
sleep($this->config['retryDelay']);
}
}
}
throw new Exception("数据库连接失败: " . $lastError->getMessage());
}
}
2 生产环境检查清单
- [ ] 数据库连接池是否配置正确
- [ ] PHP的max_execution_time是否合理
- [ ] MySQL的wait_timeout和interactive_timeout是否设置
- [ ] 是否有慢查询日志监控
- [ ] 网络防火墙是否有连接数限制
- [ ] 数据库服务器资源(CPU、内存、磁盘)是否充足
- [ ] 是否实现了自动重连机制
- [ ] 是否有监控告警系统
特殊情况处理
1 长时间运行的脚本
// 定期重置连接
while ($shouldContinue) {
try {
$pdo->query('YOUR_LONG_RUNNING_QUERY');
} catch (PDOException $e) {
if (strpos($e->getMessage(), 'has gone away') !== false) {
// 连接断开,重新连接
$pdo = connectWithRetry();
continue;
}
throw $e;
}
// 每100次查询后重新连接
if (++$queryCount % 100 === 0) {
$pdo = null;
$pdo = connectWithRetry();
}
}
2 使用数据库抽象层
考虑使用成熟的数据库抽象层如 Doctrine DBAL 或 Laravel Eloquent,它们内置了连接管理和超时处理:
// 使用Laravel配置
'connections' => [
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'read' => [
'host' => [
'192.168.1.1',
'196.168.1.2'
],
],
'write' => [
'host' => [
'196.168.1.3',
],
],
'sticky' => true,
'options' => [
PDO::ATTR_TIMEOUT => 5,
],
],
],
解决数据库连接超时问题需要:
- 合理设置超时时间:不要过大也不要过小
- 实现重试机制:避免临时网络问题影响
- 使用连接池:减少频繁创建连接的开销
- 监控和告警:及时发现并处理问题
- 优化查询和索引:减少数据库负载
- 适当配置数据库服务器:保证服务稳定
根据项目规模和需求选择最适合的解决方案组合。