
当基于 cookie 选择数据库服务器时,pdo 连接失败会导致用户被永久卡在错误页,直到 cookie 过期或手动清除;根本解法是将服务器路由信息从客户端 cookie 迁移至服务端 session,并在连接异常时动态更新 session 中的 db 配置。
当基于 cookie 选择数据库服务器时,pdo 连接失败会导致用户被永久卡在错误页,直到 cookie 过期或手动清除;根本解法是将服务器路由信息从客户端 cookie 迁移至服务端 session,并在连接异常时动态更新 session 中的 db 配置。
在 PHP 游戏或多租户架构中,常需根据用户所属“服务器”(如 server=us1、server=jp2)动态连接对应数据库。若直接将服务器标识(如 $_COOKIE['server'])用于 PDO 初始化,一旦该数据库临时不可用(宕机、网络中断、权限变更),后续所有请求都会因 new PDO(...) 抛出异常而中断——更严重的是,由于逻辑持续读取失效的 Cookie 值,即使故障已修复,用户仍无法恢复访问,必须手动清除 Cookie 或等待其过期。
核心问题在于:路由决策不应依赖不可信、不可控且难以及时刷新的客户端存储。
✅ 正确方案:使用 Session 管理数据库路由状态
Session 是服务端可控、生命周期可编程、且与用户会话强绑定的存储机制。我们将数据库服务器标识从 $_COOKIE['server'] 迁移至 $_SESSION['db_server'],并在连接失败时主动修正它,而非被动等待客户端干预。
实施步骤
-
初始化阶段:从 Cookie 读取一次,写入 Session(仅首次)
session_start(); if (!isset($_SESSION['db_server']) && isset($_COOKIE['server'])) { $_SESSION['db_server'] = filter_var($_COOKIE['server'], FILTER_SANITIZE_STRING); } -
连接数据库时捕获异常,并智能降级/切换
function connectToDB(): PDO { $servers = ['us1' => 'mysql:host=us1-db;dbname=game', 'jp2' => 'mysql:host=jp2-db;dbname=game', 'fallback' => 'mysql:host=primary-db;dbname=game']; $target = $_SESSION['db_server'] ?? 'fallback'; try { return new PDO($servers[$target], $user, $pass, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ]); } catch (PDOException $e) { // 记录错误日志(可选) error_log("DB connection failed for {$target}: " . $e->getMessage()); // 尝试切换至备用服务器(例如 fallback) $fallback = 'fallback'; if ($target !== $fallback) { $_SESSION['db_server'] = $fallback; // 重试连接(避免递归,此处显式调用) return connectToDB(); // 或直接 new PDO($servers[$fallback], ...) } throw new RuntimeException('All database servers are unavailable.'); } } -
(可选)提供手动重选服务器接口
若业务允许用户切换区服,可通过安全 API 更新 $_SESSION['db_server'],无需操作 Cookie:if ($_POST['action'] === 'switch_server' && in_array($_POST['server'], ['us1','jp2','eu3'])) { $_SESSION['db_server'] = $_POST['server']; http_response_code(200); exit(json_encode(['success' => true])); }
⚠️ 注意事项
- 绝不直接 setcookie(..., '', time()-1000) 全局清空 Cookie:这会破坏登录态(如 PHPSESSID)、语言偏好等其他关键 Cookie,造成更严重的用户体验断裂。
- Session 必须开启且配置合理:确保 session_start() 在任何输出前调用;生产环境建议使用 Redis/Memcached 存储 Session,避免文件锁瓶颈。
- Cookie 仅作初始引导,不参与运行时决策:可保留 $_COOKIE['server'] 用于首次访问识别,但后续完全以 $_SESSION['db_server'] 为准。
- 增加健康检查与缓存策略(进阶):对已知故障 DB 可在 Session 中标记 db_unavailable_until 时间戳,避免高频重试。
通过将路由状态上收至 Session,系统获得“自我修复”能力:单点 DB 故障不再阻断用户,服务恢复后 Session 自动生效,真正实现高可用与无缝降级。









