codeigniter 4 不支持运行时自动数据库故障转移,failover 仅用于启动时连接兜底;需手动捕获异常、关闭连接并显式切换至备用库,且 .env 中 failover 必须用 json 格式扁平书写。

CodeIgniter 4 的数据库 failover(故障转移)配置不生效,根本原因通常是它压根没被框架识别或触发——CI4 官方并不原生支持多数据库实例的自动 failover 切换机制。
CI4 的数据库配置设计是「单主连接 + 可选备用连接」,但这个“备用”不是运行时自动兜底的 failover,而是需要你手动调用、显式切换。很多人误以为只要在 .env 或 Database.php 里配了多个组,CI4 就会像某些 ORM 那样自动探测主库失败后切到备库,结果发现完全没反应。
数据库配置里写了 failover 组,但代码从不走备用库
CI4 的 database.default.failover 是一个数组,只用于初始化时尝试连接的候选列表,不是运行时监控+切换。它的行为是:
- 框架启动时,按顺序尝试连接
failover[0]→failover[1]→ … - 一旦某个连接成功,就固定使用它,后续所有查询都走这个连接,不会回退或轮换
- 主库恢复后也不会自动切回去,更不会在查询报错时重新挑一个 failover 连接重试
所以如果你的主库其实连得上(哪怕慢或偶尔超时),failover 组永远不会被启用。
✅ 正确用法:只在部署时做连接兜底,比如主库地址临时不可达、DNS 没生效等初始化阶段问题;不是用来应对运行中主库宕机的高可用方案。
failover 配置格式错误或路径不对
常见低级失误:
-
在
.env中写成:database.default.failover.0.hostname = "backup-db.example.com" database.default.failover.0.database = "myapp_prod"
❌ 错!CI4 不支持这种点号嵌套写法。
.env里 failover 必须用 JSON 格式扁平写:database.default.failover = '[{"hostname":"backup-db.example.com","username":"user","password":"pass","database":"myapp_prod","DBDriver":"MySQLi"}]' -
在
app/Config/Database.php中定义时,没把整个 failover 数组作为default组的子键:public $default = [ 'DSN' => '', 'hostname' => 'main-db.example.com', // ... 其他主库配置 'failover' => [ // ✅ 必须挂在这里 [ 'hostname' => 'backup-db.example.com', 'username' => 'user', 'password' => 'pass', 'database' => 'myapp_prod', 'DBDriver' => 'MySQLi' ] ] ];
查询失败时没手动触发 failover 切换
CI4 不会在 $this->db->table('users')->get() 报错后自动换库重试。你要自己捕获异常、销毁当前连接、再用 failover 里的配置新建一个:
try {
return $this->db->table('users')->get()->getResult();
} catch (\CodeIgniter\Database\Exceptions\DatabaseException $e) {
// 主库失败,手动切到第一个 failover
if (! empty($this->db->failover)) {
$this->db->close(); // 关闭当前失效连接
$this->db = \Config\Database::connect('failover'); // 假设你已定义名为 'failover' 的独立组
return $this->db->table('users')->get()->getResult();
}
throw $e;
}
注意:'failover' 是你自己在 Database.php 里额外定义的连接组名(非 default.failover 数组),CI4 并不提供 connect('default.failover.0') 这种语法。
PHP 扩展或驱动不支持连接复用与重试
即使你写了切换逻辑,以下情况也会让 failover “看起来无效”:
- 使用
MySQLi驱动时,mysqli_connect()成功返回资源后,框架不会主动检测连接是否中途断开;只有执行查询时报错,你才感知到 - 没开启
pconnect(持久连接),每次请求都是新连接,failover 只影响本次请求的初始化,无法跨请求维持“主失效→切备”的状态 - 你的 failover 库本身也连不上(权限未开、防火墙拦、MySQL 用户没授权),但错误日志只显示“主库连不上”,掩盖了备库同样失败的事实
建议加一层基础连通性验证:
// 部署前或健康检查接口里跑一下
$test = @mysqli_connect($host, $user, $pass, $db, 3306);
if (! $test) {
log_message('error', "Failover DB unreachable: " . mysqli_connect_error());
}











