CI4中无法自动识别“刚写完就得读”场景,必须由业务逻辑显式标记强一致性读(如注册后立即查询),通过手动切换主库连接(如Database::connect('default'))实现,不可依赖框架自动路由。

CI4 里怎么识别「刚写完就得读」的场景
CodeIgniter 4 本身不感知 MySQL 主从结构,query()、insert()、update() 这些方法默认走的是配置里的主库连接。所谓「写后立即读」不是框架能自动判断的,得靠业务逻辑显式标记。
常见误判点:用户注册成功后跳转到个人页,页面里查 users 表显示昵称——这看起来是「写后读」,但实际可能走从库,导致查不到刚插入的记录。
- 必须明确区分「强一致性读」和「最终一致性读」:订单创建、支付状态变更、密码重置确认这类操作,后续读取必须走主库
- CI4 没有内置的「事务内强制主库读」机制,
$db->transStart()只保证写操作原子性,不控制后续select走哪条连接 - 推荐做法是在 Service 层或 Controller 中用
$this->db = \Config\Database::connect('writable')显式切换连接,而不是依赖全局配置
如何在 CI4 中动态切换主库/从库连接
CI4 的数据库配置支持多组连接,但默认不提供运行时路由逻辑。你得自己封装一层,避免每次手动调用 \Config\Database::connect()。
一个轻量可行的做法是扩展 BaseModel,加个 $useMaster = false 属性:
class UserModel extends BaseModel
{
protected $useMaster = false;
public function find($id = null)
{
$db = $this->useMaster ? \Config\Database::connect('default') : $this->db;
return $db->table($this->table)->where('id', $id)->get()->getRow();
}
}
这样在需要强一致读的地方,只需:
$userModel = new UserModel(); $userModel->useMaster = true; $user = $userModel->find(123);
- 不要改 CI4 原生
BaseModel,继承它并覆盖关键方法即可 - 连接名(如
'default')需与app/Config/Database.php中定义的主库配置键一致 - 从库连接建议单独配成
'readonly',并在模型中用\Config\Database::connect('readonly')获取
seconds_behind_master 不为 0 时该不该阻塞请求
直接查 seconds_behind_master 并 sleep 等待,是反模式。CI4 请求生命周期短,等几秒不仅拖慢响应,还可能因超时中断。
更务实的做法是:只对极少数关键路径做延迟容忍判断,且用「降级 + 提示」代替硬等待。
- 比如用户修改邮箱后立即查看验证状态,可先查从库;若查不到,再查主库,而不是先 sleep 1s
- 查主库失败才 fallback 到缓存或本地 session 数据,避免 DB 成为单点瓶颈
-
SHOW SLAVE STATUS返回的Seconds_Behind_Master是瞬时值,网络抖动或大事务会导致它剧烈波动,不能作为稳定阈值依据 - 真正要监控的是持续 >5s 的延迟,这种应触发告警而非业务层干预
为什么不能依赖 CI4 的 Query Builder 自动读写分离
CI4 的 Query Builder 是语法构造器,不参与连接选择。它生成的 SQL 无论 SELECT 还是 INSERT,都发给当前绑定的 $db 实例——而这个实例在初始化时就固定了连接池。
这意味着:
-
$builder->select()->from('orders')->where('id', 1)->get()永远不会自动切到从库,除非你提前把$builder->db指向从库连接 - 没有中间件或 AOP 支持,无法像 ShardingSphere 或 MyCat 那样基于 SQL 类型自动路由
- 如果强行在
BaseController里根据请求路径做连接切换,容易漏掉异步任务、队列回调、定时脚本等非 HTTP 上下文场景
复杂点在于:读写分离不是加个配置就能开箱即用的事。它要求你在每个数据访问入口处,明确声明「这次读要不要等主从同步完成」——而这个决策,只能由业务语义驱动,没法交给框架猜。

















