You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP mysqli如何重连已更新的DigitalOcean托管数据库实例

问题核心原因

你遇到的是PHP-FPM常驻进程场景下的两个核心问题:

  • 你的数据库连接逻辑只判断静态连接对象是否存在,不校验连接有效性,连接失效后不会主动重建
  • 常驻的PHP-FPM进程、mysqli实例、系统DNS缓存会留存旧的数据库节点IP,故障切换后不会自动更新
解决方案

1. 修复数据库连接逻辑(核心调整)

你当前的代码存在两个明显缺陷:一是只要静态属性\SqlClass::$connObj被初始化过,不管连接是否失效都会直接判定为已连接;二是重试时复用同一个mysqli实例,该实例会缓存首次解析的旧IP,重试也不会重新解析域名。
调整后的参考代码如下:

public static function connectToDatabase(string $dbHostName, string $dbUserName, string $dbPassword, string $databaseName): void
{
    $maxRetries = 3;
    $retryDelays = [1, 2, 10]; // 每次重试的等待秒数

    // 先校验现有连接是否有效
    if (\SqlClass::$connObj) {
        // 尝试ping检测连接存活,失败则重置连接对象
        if (!@\SqlClass::$connObj->ping()) {
            \SqlClass::$connObj->close();
            \SqlClass::$connObj = null;
        }
    }

    $hasConnected = false;
    for ($i = 0; $i < $maxRetries; $i++) {
        if (!\SqlClass::$connObj) {
            // 每次重连都新建mysqli实例,避免旧实例缓存DNS解析结果
            \SqlClass::$connObj = mysqli_init();
            \SqlClass::$connObj->options(MYSQLI_OPT_CONNECT_TIMEOUT, $i === 0 ? 1 : 10);
            // 禁用MySQL客户端的DNS缓存(如支持)
            if (defined('MYSQLI_OPT_DNS_CACHE_TIMEOUT')) {
                \SqlClass::$connObj->options(MYSQLI_OPT_DNS_CACHE_TIMEOUT, 0);
            }
        }

        $hasConnected = @\SqlClass::$connObj->real_connect($dbHostName, $dbUserName, $dbPassword, $databaseName, 128);
        if ($hasConnected && !\SqlClass::$connObj->connect_errno) {
            break;
        }

        // 连接失败重置对象,等待后重试
        \SqlClass::$connObj->close();
        \SqlClass::$connObj = null;
        if ($i < $maxRetries -1) {
            sleep($retryDelays[$i]);
        }
    }

    if (!$hasConnected || \SqlClass::$connObj->connect_errno) {
        // 执行错误处理逻辑
        exit;
    }
    \SqlClass::$connObj->select_db($databaseName);
    \SqlClass::$connObj->options(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, true);
    \SqlClass::$connObj->set_charset('utf8mb4');
}

2. 调整PHP配置

编辑php.ini文件,调整以下参数,缩短PHP层面的DNS缓存时间:

; 降低PHP DNS缓存时长为10秒,默认通常为120秒
dns_cache_timeout = 10

重启PHP-FPM生效。

3. 调整PHP-FPM配置

编辑你站点对应的PHP-FPM池配置文件(通常在/etc/php/8.0/fpm/pool.d/目录下),添加以下参数:

; 每个worker进程处理500次请求后自动重启,避免常驻进程长期缓存旧DNS/旧连接
pm.max_requests = 500

如果你使用了mysqli持久连接(主机名前缀为p:),建议额外添加:

; 持久连接复用前强制校验有效性
mysqli.allow_persistent = On
mysqli.check_peristent = On

重启PHP-FPM生效。

4. 系统DNS缓存调整

如果你的服务器开启了nscd、systemd-resolved等DNS缓存服务,将A记录的缓存时长调整为10秒以内,避免系统层面长期留存旧的数据库IP。

完成以上调整后,数据库故障切换时最长30秒内即可自动恢复连接,不需要手动重启PHP-FPM。


内容的提问来源于stack exchange,提问作者Redzarf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 03:12:02