CodeIgniter 4读写分离需在.env中定义read/write两组连接,手动通过\Config\Database::connect('read')或('write')调用,框架不自动路由SQL;须在Services.php中注册服务并注意事务跨库失效与主从延迟问题。

数据库读写分离怎么配,不是改 Database.php 就完事
CodeIgniter 4 的数据库配置完全绕过 app/Config/Database.php,只认根目录下的 .env 文件。读写分离必须靠多组连接定义 + 手动切换,框架不自动路由 SELECT/INSERT 到不同库。
在 .env 中添加两套配置:
database.read.hostname = mysql-read database.read.username = app_ro database.read.password = ro_pass database.read.database = myapp_db database.write.hostname = mysql-write database.write.username = app_rw database.write.password = rw_pass database.write.database = myapp_db
注意:read 和 write 是自定义分组名,不是关键字;hostname 必须是 Docker 容器名或真实 IP,localhost 在容器里会连自己,不是宿主机或另一台 DB。
使用时手动指定分组:
-
$dbRead = \Config\Database::connect('read');获取只读连接 -
$dbWrite = \Config\Database::connect('write');获取读写连接 - 模型里不能直接用
$this->db,得显式传入连接实例或重写initDb()
服务容器里怎么注册带环境差异的数据库连接
如果想让 service('db.read') 或 service('db.write') 可用,得去 app/Config/Services.php 手动绑定。别指望容器自动识别 .env 里的分组名。
在 Services::custom() 方法里加:
$container->set('db.read', function ($container) {
return \Config\Database::connect('read');
});
$container->set('db.write', function ($container) {
return \Config\Database::connect('write');
});
这样就能在控制器里写 $readDB = service('db.read'); ——但要注意:每次调用都会新建连接,高频场景建议复用实例,别在循环里反复 service()。
环境变量要兜底:
- 用
env('DB_READ_HOST', 'localhost')替代硬编码 host - 密码类敏感项必须走
.env,且.gitignore要确保它不被提交 - 开发环境可把 read/write 指向同一个库,避免本地跑不起来
Docker 部署时 MySQL 连不上,90% 是网络和权限问题
容器内 PHP 应用连不上 MySQL,错误通常是 Connection refused 或 Access denied,和 CI4 配置关系不大,先排查底层。
确认三件事:
- Docker Compose 里 MySQL 服务暴露了正确端口(
3306),且depends_on不等于“已就绪”,得加健康检查或启动延迟 - MySQL 用户授权用了
'app_rw'@'%',不是'app_rw'@'localhost'——容器间通信走的是网桥 IP,不是 localhost - PHP 容器里能
ping mysql-write通,且telnet mysql-write 3306有响应(如没 telnet,用nc -zv mysql-write 3306)
CI4 的 CI_ENVIRONMENT=production 下错误会被静默吞掉,务必设为 development 或捕获 $db->error() 才能看到真实报错。
读写分离后事务失效、主从延迟怎么应对
一旦拆成两个物理连接,$dbWrite->transStart() 开启的事务对 $dbRead 完全不可见。这不是 CI4 的 bug,是 MySQL 架构限制。
实际处理方式只有两种:
- 写操作后强制查主库:所有涉及刚写入数据的后续查询,必须显式用
$dbWrite->table(...)->get(),不能图省事切到 read 连接 - 接受短暂延迟:对非关键数据(如统计数、日志列表),允许从从库读,但要在业务层加注释说明“可能滞后 1–3 秒”
没有银弹。所谓“自动读写分离中间件”在 CI4 生态里并不存在,强行封装只会掩盖问题。真正要动的是代码里每个 $this->db 的调用点——你得清楚知道这一行是在读还是在写。


















