CodeIgniter 4 不支持开箱即用的读写分离和 insert_batch() 等批量方法,因其数据库层重构为轻量 PDO 抽象,移除了 CI3 的批量操作及自动路由逻辑,需手动管理多连接、编写原生 SQL 并显式控制读写路径。

CodeIgniter 4 不支持开箱即用的读写分离,也**不提供内置的 insert_batch() 等批量方法**——这些是 CI3 的 Query Builder 特性,CI4 已移除。想实现读写分离或高效批量写入,必须绕过默认封装,直接操作底层连接或使用原生 SQL + 事务控制。
为什么 CI4 没有 insert_batch() 和自动读写分离
CI4 的数据库层彻底重构为基于 PDO 的轻量抽象,Query Builder 被大幅精简:insert_batch()、update_batch()、delete_batch() 全部被移除;同时,框架未定义“读库”“写库”的概念,\Config\Database::connect() 只返回一个连接实例,没有路由逻辑。
这意味着:
- 批量插入必须手写
INSERT INTO ... VALUES (),(),()或用$db->query()执行原生语句 - 读写分离需手动管理多个连接配置,并在业务代码中显式选择——比如读操作调用
\Config\Database::connect('slave'),写操作用'master' - CI4 的
.env文件只允许定义一组默认连接(database.default.*),多库需额外扩展
在 .env 中配置主从连接组(非官方但可行)
CI4 官方不支持多连接组名(如 slave / master),但你可以利用其配置加载机制,在 app/Config/Database.php 中手动定义多个连接:
先在 .env 中补充从库配置:
database.slave.hostname = 192.168.1.100 database.slave.username = reader database.slave.password = r34d0nly database.slave.database = myapp_db database.slave.port = 3307
然后修改 app/Config/Database.php,在 public function __construct() 内添加:
if (ENVIRONMENT === 'production') {
$this->slave = new \CodeIgniter\Database\MySQLi\Connection([
'DSN' => '',
'hostname' => $_ENV['database.slave.hostname'] ?? 'localhost',
'username' => $_ENV['database.slave.username'] ?? '',
'password' => $_ENV['database.slave.password'] ?? '',
'database' => $_ENV['database.slave.database'] ?? '',
'port' => (int) ($_ENV['database.slave.port'] ?? 3306),
'DBDriver' => 'MySQLi',
'DBPrefix' => '',
'pConnect' => false,
'DBDebug' => (bool) ($_ENV['database.slave.DBDebug'] ?? false),
'charset' => 'utf8mb4',
'DBCollat' => 'utf8mb4_general_ci',
'swapPre' => '',
'encrypt' => false,
'compress' => false,
'strictOn' => true,
'failover' => [],
'port' => 3307,
]);
}
这样你就能在模型中按需调用:
$master = \Config\Database::connect(); // 默认主库
$slave = service('database')->getConnection('slave'); // 需确保 service 注册了该连接
⚠️ 注意:service('database') 默认不识别自定义连接名,必须在 app/Config/Services.php 中注册新服务或直接 new Connection 实例。
手动实现批量插入(替代 insert_batch())
CI4 推荐用原生 SQL + 参数绑定做批量写入,比拼接字符串更安全:
例如插入 100 条用户记录:
$sql = "INSERT INTO users (name, email, created_at) VALUES (?, ?, ?)";
$batchData = [];
foreach ($users as $u) {
$batchData[] = [$u['name'], $u['email'], date('Y-m-d H:i:s')];
}
$db = \Config\Database::connect();
$db->query($sql, $batchData); // ✅ 支持批量参数绑定
关键点:
-
$db->query()第二个参数接受二维数组,会自动展开为多组绑定值 - 单条
INSERT最多支持约 1000–2000 行(取决于 MySQL max_allowed_packet),超量需分片 - 不要用字符串拼接生成
VALUES (...),(...)—— 易触发 SQL 注入且难维护 - 若需事务保障,用
$db->transStart()包裹整个批量操作
读写分离最容易被忽略的三个细节
很多人配完主从连接就以为完成了,结果线上出问题才意识到:
- 从库延迟没监控:CI4 不自动检测
Seconds_Behind_Master,应用层需自己查SHOW SLAVE STATUS并 fallback 到主库读取 - 事务内混用连接:在一个事务中用了
$master->transStart(),却误用$slave->table()查询,导致数据不一致或报错 “Commands out of sync” - 连接复用失效:每次调用
\Config\Database::connect('slave')都新建连接,没设pConnect => true或连接池,高并发下快速耗尽 MySQL 连接数
真正稳定的读写分离,不是靠框架自动切换,而是靠你在关键路径上明确区分「哪些查询可读从库」「哪些必须走主库」,并把这种判断固化进 DAO 层,而不是散落在控制器里。


















