ci4需手动配置主从数据源并实现读写分离:在database.php中定义'default'(主库)和'slave'(从库)两组独立连接,业务层通过db()和db('slave')显式路由读写请求,事务必须全程使用主库实例,强一致性读也须走主库以规避复制延迟。

CI4 怎么配置主从数据源并启用读写分离
CodeIgniter 4(CI4)本身不内置读写分离逻辑,必须手动配置多个数据库组、在业务层控制连接路由。关键不是“开启开关”,而是用 $db 实例的显式选择来分流 SQL。
- 在
app/Config/Database.php中定义两个独立数据库组:'default'(主库)、'slave'(从库),各自含完整 host/user/password 等参数 - 不要复用同一组配置改
hostname,否则DB()工厂无法区分主从实例 - CI4 的
DB()函数默认只认'default'组;读操作需显式调用DB('slave'),写操作始终用DB()或DB('default') - 若用 Query Builder,注意
$this->db是控制器中注入的单例,默认绑定'default',不能自动切换——必须在方法内重新调用DB('slave')获取从库实例
为什么用 DB('slave') 查询后事务会报错
CI4 的事务机制($db->transStart())是连接绑定的:一旦在某个 $db 实例上调用,后续所有查询都强制走该连接。若你在事务块里混用了 DB('slave') 和 DB(),就会触发「Cannot execute queries while in a transaction on a different connection」错误。
- 事务只能在主库执行,从库必须设为
read_only = 1(MySQL 层面),否则即使你写了UPDATE也会被拒绝 - 所有涉及写、DDL、事务的操作,必须全程使用同一个
DB()实例(即主库) - 读写混合逻辑(如“先写再查最新值”)不能靠从库读取,必须强制走主库,否则可能读到旧数据或直接失败
如何避免手动写 DB('slave') 导致漏切或误切
硬编码 DB('slave') 容易遗漏、难维护。推荐封装一层轻量路由逻辑:
- 新建
app/Database/DBRouter.php,提供静态方法read()和write() -
read()内部返回DB('slave')(可加简单负载均衡,比如轮询多个从库组) -
write()返回DB(),确保写操作永远落在主库 - Controller 或 Service 中统一调用
DBRouter::read()->table(...)->get(),而非裸写DB('slave') - 注意:不要在模型(Model)中预设
$DBGroup = 'slave',因为 Model 默认只支持单组,且无法动态切换
主从延迟导致刚写完就读不到怎么办
MySQL 主从复制默认异步,Seconds_Behind_Master 可能为几十毫秒到数秒。CI4 层面无法消除延迟,只能规避:
- 对强一致性读(如注册后立即显示用户信息),必须走主库:用
DBRouter::write()查询,哪怕只是SELECT - 不要依赖从库做「写后即读」校验,这类逻辑应放在主库事务内完成
- 若使用中间件(如 MyCat),可通过注释提示路由,例如
/<em>+ db_type:master </em>/ SELECT ...,但 CI4 原生 Query Builder 不支持自动注入该 hint,需手写$db->query()
主从同步本身没问题,问题永远出在「你以为它实时,其实它不实时」——应用层必须对读写分离有明确的语义认知,而不是把 CI4 当成自动分流的黑盒。











