codeigniter 4 不支持开箱即用的多数据库连接或读写分离,因 database.php 被忽略、.env 仅解析 default 配置、database::connect() 硬编码只读 default 组且不接受参数,无法动态加载 slave 数据库;必须通过原生 mysqli/pdo 连接并手动桥接 query builder,或借助 proxysql 等代理层实现读写分离。

CodeIgniter 4 不支持开箱即用的多数据库连接或读写分离——Database.php 被完全忽略,.env 只允许定义一套 default 配置,框架底层也未提供 $this->load->database('slave') 这类 CI3 风格的动态加载机制。
CI4 中为什么不能像 CI3 那样直接 load 多个数据库
CI4 的数据库初始化在 \Config\Database::connect() 中硬编码为只读取 default 组;Database.php 文件仅用于生成默认配置骨架,实际连接参数全部来自 .env,且不解析 database.slave.* 这类自定义键名。试图在 .env 里加 database.slave.hostname 不会生效,框架会静默跳过。
常见错误现象:Call to a member function query() on null 或 Undefined property: App\Controllers\MyController::$slave_db —— 因为 $this->slave_db 根本没被创建。
- CI4 的
Database类是单例 + 静态工厂模式,不支持运行时切换或并行实例化多个独立连接对象 -
\Config\Database::connect('slave')会报错:该方法不接受参数,签名固定为connect(): BaseConnection - 即使手动 new mysqli() 或 PDO 实例,也无法复用 CI4 的 Query Builder、事务管理、防注入等能力
可行方案:用原生连接 + 手动桥接 Query Builder
若必须在 CI4 中操作第二个数据库(如日志库、报表库),唯一可靠方式是绕过框架封装,用原生连接,并把查询结果手动喂给 Query Builder 的 set() / insertBatch() 等方法做后续处理。
使用场景:跨库同步、离线报表生成、审计日志写入等非主业务路径,不要求事务一致性或实时 JOIN。
- 在模型中新建连接:
$slave = new \mysqli('192.168.1.20', 'loguser', 'pass', 'logdb'); - 执行原始查询:
$result = $slave->query("SELECT * FROM events WHERE created_at > NOW() - INTERVAL 1 DAY"); - 将结果转为数组后交给 CI4 的 Query Builder:
$this->db->table('local_cache')->insertBatch($rows); - 务必手动关闭连接:
$slave->close();,否则可能触发 MySQL “Too many connections” 错误
注意:mysqli 和 PDO 需提前确认 PHP 已启用对应扩展,且字符集显式设为 utf8mb4,避免乱码。
读写分离只能靠中间件或代理层实现
CI4 应用层无法自动识别 SELECT 和 INSERT/UPDATE 并路由到不同库——它没有 SQL 解析器,也不挂钩执行前的 query hook。所谓“透明读写分离”,在 CI4 中必须外移。
推荐做法:用数据库代理(如 ProxySQL、MySQL Router)或负载均衡器(如 HAProxy)做 TCP 层路由,让所有应用请求发往代理地址,由代理按语句类型分发到主从节点。
- 应用只需配置一个
database.default.hostname = "proxysql",完全无感 - ProxySQL 支持基于正则匹配 SQL 类型、权重轮询、延迟检测、自动故障转移
- 避免在 PHP 层做判断:PHP 解析 SQL 易出错(如注释里的 SELECT、字符串里的 UPDATE),且增加 CPU 开销
- CI4 日志中看到的
Query:记录仍是原始语句,不体现路由路径,调试需查 ProxySQL 日志
CI4 多环境数据库切换的真实限制
.env 支持通过 CI_ENVIRONMENT=production 切换环境,但 CI4 的 Database 类不会自动加载 database.production.* —— 它只认 database.default.*。所谓“多环境多库”,本质是不同环境部署时,人工替换 .env 中的 default 值。
容易踩的坑:
- 误以为
database.development.hostname和database.production.hostname可共存于同一.env文件并自动生效 —— 实际只读default下的值 - 用 Git 管理
.env并提交敏感信息,正确做法是.gitignore掉.env,用部署脚本注入环境变量 - 在 Docker Compose 中用
environment:覆盖CI_ENVIRONMENT,却忘了同步注入database.default.*变量
真正需要隔离数据源时,别依赖框架配置切换,直接用不同域名或 IP 部署整套应用实例更稳妥。











