写操作进了从库最常见原因是未区分sql类型而统一使用同一连接对象,ci4的$this->db不内置读写分离逻辑,必须手动指定主库(如$this->primary_db)和从库(如$this->replica_db)连接,避免构造函数覆盖$db、事务混用跨连接及权限/网络配置错误。

为什么写操作进了从库?
最常见原因是没区分 SQL 类型,直接把所有查询都发给了同一个连接对象。CI4 的 $this->db 默认只绑一个连接,不会自动识别 INSERT 或 SELECT 并路由——它不内置读写分离逻辑。
必须手动控制:写操作用主库连接(比如 $this->primary_db),读操作用从库连接(比如 $this->replica_db)。别指望框架自动切换。
- 检查模型里是否统一用了
$this->db,而没按需加载不同连接 - 确认没在构造函数里覆盖了
$this->db,导致后续所有操作都走错库 - 避免在事务中混用两个连接,CI4 不支持跨连接事务,
$this->primary_db->transBegin()和$this->replica_db->get()会出问题
从库查不到刚写入的数据
这是主从延迟的典型表现,不是代码 bug。CI4 本身不处理同步等待,得靠业务层兜底。
常见应对方式:
- 对强一致性场景(如注册后立刻跳转个人页),写完后改用主库查一次,再切回从库
- 避免在写操作后立即执行「相同条件」的读,可加短延时或重试逻辑(慎用)
- 监控
Seconds_Behind_Master,延迟超阈值时临时降级读主库
配置了多连接但报 “Call to a member function on null”
错误通常出现在 $this->load->database('xxx', TRUE) 返回 null,根本原因是 autoinit => FALSE 或连接参数错误。
排查重点:
- 确认
database.php中对应连接组的autoinit设为TRUE,否则加载后不初始化,调用方法时就是 null - 检查 hostname、username、database 是否拼写正确,特别是从库账号是否有 SELECT 权限
- CI4 的
load->database()不会抛异常,失败时静默返回 null,务必加判空:if (!$this->replica_db) { log_message('error', 'Replica DB failed'); }
怎么让读写分离对业务代码透明?
CI4 原生不支持透明路由,硬要实现就得重写 DB 类或封装服务层。但要注意:这种“透明”往往掩盖了主从差异,反而增加排查难度。
更务实的做法:
- 在 Service 层统一做路由判断,比如
UserService::getById()内部用从库,UserService::create()用主库 - 禁止控制器直连
$this->db,所有数据库操作必须经由 Service 方法入口 - 给连接对象命名清晰(
$this->db_primary/$this->db_replica),比抽象一层“自动路由”更容易 debug
真正容易被忽略的是权限和网络连通性——从库账号没开 SELECT、主库防火墙拦了从库 IP、甚至 DNS 解析失败,这些底层问题常被当成框架配置问题反复折腾。











