ci3和ci4只读账号配置逻辑完全不同:ci3需在application/config/database.php定义readonly数据库组并手动加载;ci4需在app/config/database.php声明$readonly配置项,通过\config\database::connect('readonly')获取实例,且修改后须执行php spark cache:clear。

CI3 和 CI4 的只读账号配置逻辑完全不同
CodeIgniter 本身不提供“自动路由读写”的内置机制,所谓“只读账号”,本质是配一个权限受限的 MySQL 用户,再在框架里指向它。但 CI3 和 CI4 的配置路径、驱动要求、加载方式全都不一样,混用会直接报错或静默失败。
CI3 仍允许用 mysql 驱动(已废弃),但只读账号必须搭配 mysqli 才能稳定工作;CI4 则强制要求 MySQLi 或 PDO,且 PDO 必须手动填 DSN 字段,漏掉就连接不上。
- CI3 配置文件路径:
application/config/database.php - CI4 配置文件路径:
app/Config/Database.php - CI4 修改后必须执行
php spark cache:clear,否则旧缓存会掩盖新配置
MySQL 层面创建只读用户是前提
只读账号不是靠 CodeIgniter 控制的,而是数据库用户权限决定的。哪怕你在 CI 里写了 SELECT,只要账号有 INSERT 权限,照样能写。所以第一步必须在 MySQL 中建一个真正只读的用户。
执行以下 SQL(替换 readonly_user 和 your_db):
CREATE USER 'readonly_user'@'%' IDENTIFIED BY 'strong_password'; GRANT SELECT ON your_db.* TO 'readonly_user'@'%'; FLUSH PRIVILEGES;
- 别用
localhost做 host,除非你的 PHP 和 MySQL 在同一台机器且走 Unix socket;多数部署用%或具体 IP 更稳妥 - 不要给
USAGE或SHOW DATABASES权限,除非调试需要 - 如果用 RDS 或云数据库,检查其白名单和账号策略是否限制了
GRANT语句
CI3 中配置只读数据库组并手动切换
CI3 不支持自动读写分离,只能靠你在 Model 里显式调用不同数据库实例。你需要定义一个名为 readonly 的数据库组,并在 Model 构造函数中加载它。
在 application/config/database.php 中添加:
$db['readonly'] = array(
'hostname' => '192.168.1.100',
'username' => 'readonly_user',
'password' => 'strong_password',
'database' => 'your_db',
'dbdriver' => 'mysqli',
'db_debug' => TRUE,
'cache_on' => FALSE,
'cachedir' => '',
'char_set' => 'utf8mb4',
'dbcollat' => 'utf8mb4_unicode_ci'
);
-
$active_group仍保持为'default',避免影响其他模块 - Model 中用
$this->readonly = $this->load->database('readonly', TRUE)加载,TRUE表示返回对象而非单例 - 调用时明确写
$this->readonly->get(),不能依赖$this->db默认实例
CI4 中用 service() 获取只读实例更安全
CI4 没有全局 $this->db,所有数据库实例都应通过 service('database') 或构造注入获取。配置只读连接的关键是:不能复用 $default 实例,必须新建一个独立配置项,并在 Database.php 中声明。
在 app/Config/Database.php 中追加:
public $readonly = [
'DBDriver' => 'MySQLi',
'hostname' => '192.168.1.100',
'username' => 'readonly_user',
'password' => 'strong_password',
'database' => 'your_db',
'charset' => 'utf8mb4',
'DBDebug' => true,
];
然后在 Model 中这样用:
protected $readonlyDB;
<p>public function initController(\CodeIgniter\Http\Request $request, \CodeIgniter\Http\Response $response, \CodeIgniter\Filters\Filters $filters)
{
parent::initController($request, $response, $filters);
$this->readonlyDB = \Config\Database::connect('readonly');
}</p>
- 别用
service('database', null, false),它只返回默认实例 -
\Config\Database::connect('readonly')是唯一可靠方式,参数名必须和配置属性名完全一致(大小写敏感) - 如果你在 Controller 里临时查只读数据,记得用完就
unset($db),避免长连接堆积
最常被忽略的一点:只读账号在事务中可能意外失败。MySQL 的 START TRANSACTION 本身不写数据,但某些驱动(尤其是 PDO)会在开启事务时尝试写入系统表或生成 savepoint,导致只读用户报错。遇到 Can't execute the given command because you have active locked tables or an active transaction 类错误,优先检查是否在只读连接上调用了 transStart() 或 beginTransaction()。











