codeigniter 4 原生不支持数据库故障转移,$db 配置仅允许单连接,需手动实现备用节点重试逻辑,且读写操作须自行路由,backup 节点若为只读将导致写操作失败。

CodeIgniter 4 原生不支持数据库故障转移
CI4 的 $db 配置数组和 Database 类本身没有内置“主从自动切换”或“备用节点 fallback”机制。你写进 app/Config/Database.php 的 $default 组,只允许一个连接参数集;传入多个 host 或用逗号分隔会被当作非法 hostname 直接报错 Connection refused 或 Unknown MySQL server host。
必须手动实现连接重试逻辑
想在连接失败后尝试备用地址,得自己封装一层连接调用,不能依赖 $this->db 直接初始化。常见做法是在服务类或基础模型里写一个带重试的工厂方法:
- 定义两个配置组:比如
$db['primary']和$db['backup'],都完整配置hostname、username、password等 - 用
try/catch包裹Database::connect('primary'),捕获ConnectionException - 首次失败后,延迟 100ms 再试
Database::connect('backup'),避免雪崩式重连 - 把返回的
ConnectionInterface实例缓存到静态属性或 DI 容器,后续查询复用它,而不是每次新建
注意:CI4 的 Database::connect() 是静态方法,但返回的连接对象不共享状态,所以重试时必须重新 connect,不能复用失败的句柄。
备用节点切换后,事务和查询行为会变
一旦切到 backup 节点,所有后续 $builder->get()、$model->find() 都走新连接——这意味着:
- 如果 backup 是只读从库,
insert()或update()会直接抛出 MySQL 错误(如ERROR 1290 (HY000): The MySQL server is running with the --read-only option) - 没有自动读写分离路由,你得自己判断操作类型再选连接组,比如写操作永远走 primary,读操作才 fallback
-
migrations表只存在于 primary,切到 backup 后执行php spark migrate会找不到表,报Table 'myapp.migrations' doesn't exist
别忽略 DNS 缓存和连接池超时
即使代码层面做了重试,底层仍可能被系统级设置卡住:
- Linux 下
/etc/nsswitch.conf若配置了dns [!UNAVAIL=return],DNS 查询失败会立即返回,不会等备用 IP;建议在hostname里直接写 IP(如'10.0.1.5'),绕过 DNS - MySQL 连接池(如使用 PDO + persistent = true)可能缓存了失效的 socket,导致 retry 仍连旧地址;CI4 默认不用持久连接,但若你手动加了
'pconnect' => true,务必设'compress' => false并确认 MySQL 服务端wait_timeout≥ PHP 端连接复用间隔 - Docker 环境中,备用节点容器名在 primary 启动时可能尚未注册进网络,
gethostbyname()返回空,需加depends_on+ healthcheck 或启动脚本等待
真正的高可用不是靠单次 fallback 解决的,而是要结合连接池健康检测、应用层路由策略、以及运维侧的 VIP 或代理(如 ProxySQL、MaxScale)——CI4 只负责执行 SQL,不负责兜底。











