CodeIgniter 4原生不支持读写分离,必须手动配置writer和reader两组数据库连接并重写Model方法:在database.php中显式定义两组独立连接(不同服务器/端口/用户/权限),Model中通过Config\Database::connect()显式指定连接,读操作走reader、写操作强制走writer,原生SQL需显式绑定连接,事务仅限writer,隐式写操作须警惕。

CodeIgniter 4 原生不支持读写分离,必须手动配置数据库组 + 在 Model 层做路由判断,否则所有查询都会打到同一个连接(通常是主库),主库压力会悄无声息地爆掉。
database.php 里怎么配主从两组连接
不能只写一个 default 组,得显式定义 writer 和 reader 两个独立组,且它们的 DSN 或连接参数必须指向不同服务器(或同一服务器不同端口/用户):
-
writer组必须设'DBDebug' => true(便于排查写操作失败)和'failover' => [](禁用自动故障转移,避免误切从库执行写) -
reader组建议开启'pconnect' => true并设较短的'port'(如 3307),方便与主库区分 - 两个组的
'username'最好不同(例如app_writer/app_reader),靠权限控制从库无法执行INSERT/UPDATE/DELETE - 别漏掉
'charset'和'DBCollat'一致性校验,字符集不一致会导致从库同步中断
Model 类里如何自动走主库 or 从库
CI4 的 Model 默认只认 default 连接,要实现读写分离,得重写 __construct() 和关键方法,而不是依赖中间件或全局钩子:
- 在自定义基类
BaseModel构造函数中,用$db =& \Config\Database::connect('writer')显式指定写连接;读操作则按需调用\Config\Database::connect('reader') - 覆盖
find()、findAll()、where()等读方法,内部统一走reader连接;而insert()、update()、delete()必须强制走writer - 注意
with()关联查询:如果关联表也需读写分离,每个关联 Model 也得走自己的reader/writer实例,不能复用父 Model 的连接 - 事务只能在
writer上开启:$this->db->transBegin(),一旦进了事务,后续所有查询都必须走同一个连接,否则报错Commands out of sync
为什么不能用 $db->query() 直接写原生 SQL
手写 $db->query('SELECT ...') 时,你根本没指定用哪个连接,它默认走 default —— 而这个 default 如果没删掉或没设为 writer,就可能把读请求发到主库,或者更糟:把写请求发到从库导致报错 ERROR 1290 (HY000): The MySQL server is running with the --read-only option。
- 所有原生查询必须显式绑定连接:
\Config\Database::connect('reader')->query('SELECT ...') - 批量插入慎用
INSERT ... SELECT:这类语句在 MySQL 中属于“写操作”,即使语法像读,也必须走writer -
REPLACE INTO、INSERT ... ON DUPLICATE KEY UPDATE全是写操作,别被名字骗了 - 视图查询(
SELECT * FROM user_view)看似只读,但如果视图底层含UNION或触发器,仍可能触发写逻辑,稳妥起见一律走writer并加注释说明
最易被忽略的是「隐式写」:比如 LAST_INSERT_ID()、SELECT ... FOR UPDATE、甚至某些带 UUID() 函数的查询,MySQL 会将其归类为写操作并拒绝在只读从库执行。上线前务必用慢日志 + SHOW PROCESSLIST 抓一次真实流量,确认所有 SELECT 确实落在从库,所有 INSERT/UPDATE/DELETE 没一个漏网。


















