codeigniter 4原生不支持读写分离,需通过自定义db类、定义master/slave连接组(autoinit=>false)、sql类型判断(select走从库,insert/update等走主库)、连接缓存及事务强制主库来实现透明读写分离。

CodeIgniter 4 原生不支持读写分离,必须通过自定义数据库类 + 多连接组 + SQL 类型判断来实现,且不能依赖 autoinit => TRUE,否则主从连接会提前建立、浪费资源。
怎么在 database.php 里配置主从连接组
主从不是“一个配置开个开关”,而是定义两个独立连接组:一个标为 'master',一个或多个标为 'slave'(命名任意,但需语义清晰)。
-
$db['master']必须包含写操作所需参数:hostname、username、password、database、dbdriver;autoinit设为FALSE,避免启动时就连主库 -
$db['slave1']同理,但指向从库地址;可再加$db['slave2']实现从库负载均衡,CI4 不自动轮询,需手动实现 -
pconnect建议统一设为FALSE:主从通常跨服务器,持久连接反而易因网络抖动残留失效连接 - 所有连接组的
char_set和dbcollat必须一致,否则跨库 JOIN 或字符比较可能出错
为什么不能直接改 system/database/DB.php
CI4 的核心类不允许直接修改,否则升级时会被覆盖。正确做法是继承并替换加载逻辑 —— 在 app/Database/DB.php 中创建自定义类,并通过 services.php 注入。
- 新建
app/Database/DB.php,继承CodeIgniter\Database\DB,重写connect()方法,在其中根据当前 SQL 类型(INSERT、UPDATE、DELETE)决定用master还是随机选一个slave - 在
app/Config/Services.php的database方法中,返回你自定义的 DB 类实例,而不是默认的CodeIgniter\Database\DB - 关键点:
connect()必须缓存已建立的连接对象(如$this->masterConnection、$this->slaveConnections),避免每次查询都重连 - 不要在构造函数里初始化连接,而应在首次执行查询前触发 —— 这才是真正的“按需连接”
模型里怎么写才能透明支持读写分离
目标是开发者仍用 $this->db->table('users')->get(),底层自动走从库;用 insert()、update() 自动走主库 —— 这需要重写 Query Builder 的执行入口。
- 在自定义 DB 类中,拦截
query()和所有 Query Builder 方法(get()、insert()等),提取原始 SQL 并用正则判断类型:/^\s*(SELECT|SHOW|EXPLAIN)/i→ 从库;/^\s*(INSERT|UPDATE|DELETE|REPLACE|ALTER|CREATE|DROP|TRUNCATE)/i→ 主库 - 注意
CALL存储过程、SET变量等语句,它们既非纯读也非纯写,建议统一走主库,避免从库权限不足报错 - 事务必须强制走主库:
startTransaction()调用后,后续所有操作(包括get())都应绑定到同一主库连接,否则会抛出SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active - 不要在模型中手动调用
$this->load->database('master', true),这会破坏透明性,也绕过你的路由逻辑
容易被忽略的三个硬伤
读写分离不是配完就完事,CI4 下有三个隐蔽但致命的问题:
- 从库延迟:CI4 不提供“强制走主库”的快捷方法,比如刚
insert()完立刻get(),可能查不到数据。必须显式用$this->db->useMaster()->get()(需你在自定义类中实现该链式方法) - 连接复用失效:如果一个请求里混合了读写操作,而你的连接缓存没做隔离(比如共用同一个
$this->conn属性),第二次get()可能复用了刚执行过insert()的主库连接,白白浪费从库资源 - 错误日志误导:CI4 默认把所有 DB 错误都记到
app/Logs/下,但你无法区分是主库挂了还是某个从库宕机 —— 建议在自定义 DB 类中捕获异常时,打上来源标记,例如[master] Connection refused或[slave1] Query timeout











